├── .devcontainer └── devcontainer.json ├── .gitignore ├── .gitmodules ├── Ansible └── XML │ ├── README.md │ ├── filter_plugins │ └── make_list.py │ ├── get-junos.yml │ ├── get-nxos.yml │ └── topology.yml ├── BGP ├── AS-prepend │ ├── ansible.cfg │ ├── bgp-as-path.j2 │ ├── hosts-bgp-as.yml │ └── initial-config.j2 ├── DMZ-BW │ ├── .gitignore │ ├── BGP-Link-Bandwidth-Lab.png │ ├── README.md │ ├── config │ │ ├── e1.cfg │ │ ├── e2.cfg │ │ ├── pe1.cfg │ │ ├── x1.cfg │ │ └── x2.cfg │ ├── core-bgp.j2 │ ├── edge-bgp.j2 │ └── lab.yml ├── Default-NH │ ├── ospf-default │ │ ├── eos.j2 │ │ └── frr.j2 │ └── topology.yml ├── EBGP-policy │ ├── 8212-default │ │ └── frr.j2 │ ├── README.md │ ├── ebgp-policy.png │ └── topology.yml ├── IBGP-local-AS │ ├── initial.yml │ └── initial │ │ ├── dut.cfg │ │ ├── x1-daemons │ │ ├── x1.cfg │ │ ├── x2-daemons │ │ └── x2.cfg ├── IGP-metric │ ├── README.md │ ├── Vagrantfile │ ├── config │ │ ├── e1.cfg │ │ ├── e2.cfg │ │ └── pe1.cfg │ ├── topology-2igp.yml │ ├── topology-isis.yml │ └── topology-ospf.yml ├── LocPref-Prepend │ ├── ansible.cfg │ ├── clab.yml │ ├── graph.dot │ ├── group_vars │ │ └── frr │ │ │ └── topology.yml │ ├── host_vars │ │ ├── c1 │ │ │ └── topology.yml │ │ ├── c2 │ │ │ └── topology.yml │ │ ├── pe1 │ │ │ └── topology.yml │ │ ├── pe2 │ │ │ └── topology.yml │ │ ├── pe3 │ │ │ └── topology.yml │ │ └── x1 │ │ │ └── topology.yml │ ├── hosts.yml │ └── topology.yml ├── LocalAS-Migration │ ├── README.md │ ├── initial │ │ ├── custa.cfg │ │ ├── custb.cfg │ │ ├── ispa.cfg │ │ ├── r1.cfg │ │ └── r2.cfg │ ├── migrated.yml │ ├── migrated │ │ ├── custa.cfg │ │ ├── custb.cfg │ │ ├── ispa.cfg │ │ ├── r1.cfg │ │ └── r2.cfg │ └── topology.yml ├── Multi-Loopback │ ├── bgp-ipv6.eos.j2 │ ├── bgp-ipv6.ios.j2 │ ├── bgp-ipv6.nxos.j2 │ ├── loopback.eos.j2 │ ├── loopback.ios.j2 │ ├── loopback.nxos.j2 │ └── topology.yml ├── Multipath │ ├── .gitignore │ ├── baseline.yml │ ├── bgp-addpath.j2 │ ├── test-loopback.j2 │ └── topology.yml ├── Multipath_sros │ ├── Multipath_sros.PNG │ ├── README.md │ ├── anycast_topology.yml │ ├── bgp-addpath │ │ ├── srlinux.j2 │ │ └── sros.j2 │ ├── bgp-anycast.j2 │ └── topology.yml ├── RR-cluster-id │ ├── README.md │ ├── bgp.png │ └── topology.yml ├── RR-next-hop-self │ ├── BGP-RR-next-hop-self-topology.png │ ├── README.md │ ├── eos-rr-next-hop-self-fixed.tar.gz │ ├── eos-rr-next-hop-self.tar.gz │ ├── graph.dot │ └── topology.yml ├── RR-sessions │ ├── README.md │ ├── bgp.png │ └── topology.yml ├── Route-Leaks │ ├── README.md │ ├── graph-rank.yml │ ├── leak-practice-lab.png │ ├── nice-graph.yml │ └── topology.yml ├── Sessions │ ├── README.md │ ├── gtsm.yml │ ├── nofake.py │ ├── router_id.yml │ └── unknown-source-ip.yml ├── UCMP-anycast │ ├── README.md │ ├── anycast.j2 │ ├── bw-ucmp.j2 │ ├── dmz-bw.j2 │ └── ospf+ibgp.yml ├── Unnumbered │ ├── README.md │ ├── graph.png │ └── topology.yml ├── bgpipe │ ├── Dockerfile │ ├── README.md │ └── topology.yml └── interface-IBGP │ ├── README.md │ ├── config │ ├── leaf-1.cfg │ ├── leaf-2.cfg │ ├── spine-1.cfg │ └── spine-2.cfg │ ├── ibgp.interface │ └── plugin.py │ ├── ibgp.nhs │ ├── eos.j2 │ └── frr.j2 │ └── topology.yml ├── DHCP ├── evpn-redundant-server │ ├── README.md │ ├── clear-log.sh │ ├── config │ │ ├── cl_a.cfg │ │ ├── cl_b.cfg │ │ ├── srv1.cfg │ │ ├── srv2.cfg │ │ ├── sw1.cfg │ │ ├── sw2.cfg │ │ └── sw3.cfg │ ├── dhcp-client │ │ └── ios.j2 │ ├── dhcp-relay │ │ └── eos.j2 │ ├── dhcp-server.j2 │ ├── evpn-dhcp-redundant-server.png │ ├── fetch-log.sh │ ├── logs │ │ ├── release-cl_a.log │ │ ├── release-srv1.log │ │ ├── release-srv2.log │ │ ├── renew-cl_a.log │ │ ├── renew-srv1.log │ │ ├── renew-srv2.log │ │ ├── request-cl_a.log │ │ ├── request-srv1.log │ │ └── request-srv2.log │ ├── outputs │ │ └── format.yml │ └── topology.yml ├── evpn-relay-v2 │ ├── README.md │ ├── dhcp-client │ │ ├── alpine.interfaces │ │ └── linux.j2 │ ├── dhcp-relay │ │ └── srlinux.j2 │ ├── dhcp-server │ │ ├── linux.j2 │ │ └── sros.j2 │ ├── dhcpd.j2 │ ├── dnsmasq.j2 │ ├── evpn-dhcp-relay-v2.png │ ├── outputs │ │ └── format.yml │ └── topology.yml ├── evpn-relay │ ├── README.md │ ├── config │ │ ├── cl_a.cfg │ │ ├── cl_a.log │ │ ├── cl_b.cfg │ │ ├── srv.cfg │ │ ├── srv.log │ │ ├── sw1.cfg │ │ └── sw2.cfg │ ├── dhcp-client │ │ └── ios.j2 │ ├── dhcp-relay │ │ └── eos.j2 │ ├── dhcp-server.j2 │ ├── evpn-dhcp-relay.png │ ├── outputs │ │ └── format.yml │ └── topology.yml ├── relay │ ├── README.md │ ├── config │ │ ├── relay.cfg │ │ ├── srv.cfg │ │ └── user.cfg │ ├── dhcp-client │ │ ├── ios.j2 │ │ ├── linux-clab.j2 │ │ ├── linux-ubuntu.j2 │ │ ├── linux-vanilla.j2 │ │ └── linux.j2 │ ├── dhcp-relay.png │ ├── dhcp-relay │ │ ├── eos.j2 │ │ ├── ios.j2 │ │ └── linux.j2 │ ├── dhcp-server │ │ ├── dnsmasq.j2 │ │ ├── ios.j2 │ │ └── linux.j2 │ ├── linux-clab.yml │ ├── linux-vm.yml │ └── topology.yml ├── vrf-relay │ ├── README.md │ ├── config │ │ ├── relay.cfg │ │ ├── srv.cfg │ │ ├── srv.log │ │ ├── user.cfg │ │ └── user.log │ ├── dhcp-client │ │ └── ios.j2 │ ├── dhcp-relay │ │ ├── eos.j2 │ │ └── ios.j2 │ ├── dhcp-server.j2 │ ├── outputs │ │ └── format.yml │ ├── topology.yml │ └── vrf-dhcp-relay.png ├── vxlan-redundant-server │ ├── README.md │ ├── clear-log.sh │ ├── config │ │ ├── cl_a.cfg │ │ ├── cl_b.cfg │ │ ├── srv1.cfg │ │ ├── srv2.cfg │ │ ├── sw1.cfg │ │ ├── sw2.cfg │ │ └── sw3.cfg │ ├── dhcp-client │ │ └── ios.j2 │ ├── dhcp-relay │ │ └── eos.j2 │ ├── dhcp-server.j2 │ ├── fetch-log.sh │ ├── logs │ │ ├── release-cl_a.log │ │ ├── release-srv1.log │ │ ├── renew-cl_a.log │ │ ├── renew-srv1.log │ │ ├── request-cl_a.log │ │ ├── request-srv1.log │ │ └── request-srv2.log │ ├── outputs │ │ └── format.yml │ ├── topology.yml │ └── vxlan-redundant-dhcp-server.png └── vxlan-relay │ ├── README.md │ ├── config │ ├── cl_a.cfg │ ├── cl_b.cfg │ ├── srv.cfg │ ├── sw1.cfg │ └── sw2.cfg │ ├── dhcp-client │ └── ios.j2 │ ├── dhcp-relay │ └── eos.j2 │ ├── dhcp-server.j2 │ ├── outputs │ └── format.yml │ ├── topology.yml │ └── vxlan-dhcp-relay.png ├── DMVPN ├── Readme.md ├── dmvpn.png ├── dmvpn_overlay.png ├── hub.j2 ├── iot.j2 ├── spoke.j2 └── topology.yml ├── EIGRP └── MTU │ ├── extra-r1.j2 │ └── topology.yml ├── EVPN ├── ebgp-ebgp │ ├── README.md │ ├── check.config │ │ └── plugin.py │ ├── ebgp.ecmp │ │ ├── eos.j2 │ │ └── frr.j2 │ ├── eos │ │ ├── L1.cfg │ │ ├── L2.cfg │ │ ├── L3.cfg │ │ ├── L4.cfg │ │ ├── S1.cfg │ │ └── S2.cfg │ ├── frr │ │ ├── L1-daemons │ │ ├── L1.cfg │ │ ├── L2-daemons │ │ ├── L2.cfg │ │ ├── L3-daemons │ │ ├── L3.cfg │ │ ├── L4-daemons │ │ ├── L4.cfg │ │ ├── S1-daemons │ │ ├── S1.cfg │ │ ├── S2-daemons │ │ └── S2.cfg │ ├── graph.d2 │ ├── graph.png │ └── topology.yml ├── ebgp │ ├── README.md │ ├── check.config │ │ └── plugin.py │ ├── ebgp.ecmp │ │ ├── eos.j2 │ │ └── frr.j2 │ ├── eos │ │ ├── L1.cfg │ │ ├── L2.cfg │ │ ├── L3.cfg │ │ ├── L4.cfg │ │ ├── S1.cfg │ │ └── S2.cfg │ ├── frr │ │ ├── L1-daemons │ │ ├── L1.cfg │ │ ├── L2-daemons │ │ ├── L2.cfg │ │ ├── L3-daemons │ │ ├── L3.cfg │ │ ├── L4-daemons │ │ ├── L4.cfg │ │ ├── S1-daemons │ │ ├── S1.cfg │ │ ├── S2-daemons │ │ └── S2.cfg │ └── topology.yml ├── ibgp-ebgp │ ├── README.md │ ├── eos │ │ ├── L1.cfg │ │ ├── L2.cfg │ │ ├── L3.cfg │ │ ├── L4.cfg │ │ ├── S1.cfg │ │ └── S2.cfg │ ├── frr │ │ ├── L1-daemons │ │ ├── L1.cfg │ │ ├── L2-daemons │ │ ├── L2.cfg │ │ ├── L3-daemons │ │ ├── L3.cfg │ │ ├── L4-daemons │ │ ├── L4.cfg │ │ ├── S1-daemons │ │ ├── S1.cfg │ │ ├── S2-daemons │ │ └── S2.cfg │ └── topology.yml ├── ibgp-full-mesh │ ├── README.md │ ├── config │ │ ├── L1.cfg │ │ ├── L2.cfg │ │ ├── L3.cfg │ │ ├── L4.cfg │ │ ├── S1.cfg │ │ └── S2.cfg │ └── topology.yml ├── ibgp-rr │ ├── README.md │ ├── config │ │ ├── L1.cfg │ │ ├── L2.cfg │ │ ├── L3.cfg │ │ ├── L4.cfg │ │ ├── S1.cfg │ │ └── S2.cfg │ └── topology.yml ├── inter-as-a │ ├── .gitignore │ ├── README.md │ ├── inter-as-bgp.png │ ├── inter-as-topology.png │ └── topology.yml ├── inter-as-c │ ├── README.md │ ├── bgp_evpn.png │ ├── bgp_v4.png │ ├── topo.png │ └── topology.yml ├── inter-as-multi-pod │ ├── README.md │ ├── multi-pod-bgp.png │ ├── multi-pod-topology.png │ └── topology.yml ├── l3vpn-cs │ ├── README.md │ ├── graph.d2 │ ├── topology.png │ └── topology.yml ├── l3vpn-hub-spoke │ ├── README.md │ ├── config │ │ ├── ce_hub-daemons │ │ ├── ce_hub.cfg │ │ ├── ce_s1-daemons │ │ ├── ce_s1.cfg │ │ ├── ce_s2-daemons │ │ ├── ce_s2.cfg │ │ ├── p.cfg │ │ ├── pe_a.cfg │ │ ├── pe_b.cfg │ │ └── pe_h.cfg │ ├── graph.d2 │ ├── reports │ │ └── vrf.md.j2 │ ├── topology.png │ └── topology.yml ├── l3vpn-uvni │ ├── README.md │ ├── graph.d2 │ ├── topology.png │ ├── topology.yml │ └── unique_vni.py ├── l3vpn │ ├── README.md │ ├── evpn-l3vpn-topology.png │ └── topology.yml ├── mpls-bridging │ ├── README.md │ ├── evpn.mpls │ │ ├── eos.j2 │ │ └── sros.j2 │ ├── saved_configs │ │ ├── p.cfg │ │ ├── pe1.cfg │ │ ├── pe2.cfg │ │ └── pe3.cfg │ ├── topology.png │ └── topology.yml ├── mpls-vlan-bundle │ ├── README.md │ ├── evpn-mpls-vlan-bundle.png │ ├── saved_config │ │ ├── p.cfg │ │ ├── pe1.cfg │ │ ├── pe2.cfg │ │ └── pe3.cfg │ └── topology.yml ├── symmetric-irb │ ├── README.md │ ├── config │ │ ├── s1.config │ │ ├── s2-daemons │ │ └── s2.cfg │ └── topology.yml ├── tshoot-multi-pod │ ├── README.md │ ├── config │ │ ├── ca.cfg │ │ ├── cb-daemons │ │ ├── cb.cfg │ │ ├── la.cfg │ │ └── lb.cfg │ ├── fabric.png │ ├── fix-nh │ │ └── cb.j2 │ ├── fix-rt │ │ └── eos.j2 │ └── topology.yml ├── vxlan-bridging │ ├── README.md │ ├── topology.png │ └── topology.yml └── vxlan-fabric │ ├── README.md │ ├── config │ ├── L1.cfg │ ├── L2.cfg │ ├── L3.cfg │ ├── L4.cfg │ ├── S1.cfg │ └── S2.cfg │ ├── evpn-design-fabric.png │ ├── evpn-design-vxlan.png │ └── topology.yml ├── ISIS └── unnumbered │ ├── README.md │ ├── topology.png │ └── topology.yml ├── LAG └── examples │ ├── 01-l3-lag.yml │ ├── 02-lag-vlan-trunk.yml │ ├── 03-l3-lag-passive.yml │ ├── 04-host-mlag.yml │ ├── 05-bonding-active-standby.yml │ ├── 06-host-mlag-anycast-gateway.yml │ ├── 07-mlag-trunk.yml │ ├── 09-mlag-m-to-m.yml │ └── 10-mlag-m-to-m-different-vlans.yml ├── MPLS ├── hub-spoke-one-arm │ ├── README.md │ ├── topology.png │ └── topology.yml ├── ldp-bgp-lu │ ├── README.md │ ├── graph.dot │ ├── topology.bgp.png │ ├── topology.link.png │ └── topology.yml ├── sr-bgp-lu │ └── topology.yml └── vpn-simple │ ├── README.md │ ├── mpls-vpn-simple.png │ └── topology.yml ├── OSPF ├── anycast │ ├── .gitignore │ ├── anycast-external │ │ ├── a1.cfg │ │ ├── a2.cfg │ │ ├── a3.cfg │ │ ├── l1.cfg │ │ ├── l2.cfg │ │ └── s1.cfg │ ├── anycast-internal │ │ ├── a1.cfg │ │ ├── a2.cfg │ │ ├── a3.cfg │ │ ├── l1.cfg │ │ ├── l2.cfg │ │ └── s1.cfg │ ├── anycast-redistribute │ │ ├── a1.cfg │ │ ├── a2.cfg │ │ ├── a3.cfg │ │ ├── l1.cfg │ │ ├── l2.cfg │ │ └── s1.cfg │ ├── ospf+bgp.yml │ ├── ospf-anycast-external.j2 │ ├── ospf-anycast.j2 │ ├── ospf-redistribute.j2 │ └── ospf.yml ├── area-range │ ├── README.md │ ├── area_range │ │ ├── eos.j2 │ │ ├── frr.j2 │ │ └── ios.j2 │ ├── graph.d2 │ ├── graph.png │ └── topology.yml ├── bfd │ └── topology.yml ├── inter-area-dv │ └── topology.yml ├── ipv4-only │ ├── README.md │ └── topology.yml ├── multi-process │ └── lab.yml ├── p2mp │ ├── initial-config.j2 │ ├── mesh-ping.yml │ ├── ospf-p2mp.j2 │ ├── topology-expanded.yml │ └── topology.yml └── unnumbered │ ├── README.md │ ├── graph.dot │ ├── topology.png │ └── topology.yml ├── README.md ├── STP └── examples │ ├── 01-vlan-loop-single.yml │ ├── 02-vlan-loop-multiple.yml │ ├── 02b-vlan-loop-trunk.yml │ ├── 03-vlan-multi-link.yml │ ├── 03b-vlan-multi-link-no-stp.yml │ ├── 03c-vlan-trunk-priority.yml │ ├── 04-disable-stp.yml │ ├── 05-mstp.yml │ ├── 06-disable-stp-per-vlan.yml │ ├── 07-pvrst-interop.yml │ ├── 08-stp-port-type.yml │ ├── 09-acc-agg-core.yml │ └── templates │ ├── bpduguard │ ├── acc-custom.eos.j2 │ └── acc-custom.nxos.j2 │ └── vlan │ └── dellos10.j2 ├── VLAN ├── l2-fabric │ ├── README.md │ ├── graph.dot │ ├── topology.png │ └── topology.yml ├── vlan-access-node │ └── topology.yml ├── vlan-access-ospf │ └── topology.yml ├── vlan-access-stretch │ ├── README.md │ ├── topology.yml │ └── vlan-simple.png ├── vlan-router-on-a-stick │ ├── README.md │ ├── netlab-router-stick.png │ ├── topology.yml │ └── vlan-trunk.png ├── vlan-trunk-vrf │ ├── README.md │ ├── topology.yml │ └── vlan-vrf.png └── vlan-trunk │ ├── README.md │ ├── topology.yml │ └── vlan-trunk.png ├── VRF ├── multihop-vrf-lite │ ├── README.md │ ├── topology.yml │ └── vrf-lite-routed.png ├── vrf-hub-spoke │ ├── README.md │ ├── graph.d2 │ ├── topology.png │ └── topology.yml ├── vrf-lite-hosts │ ├── README.md │ ├── multi-vrf.yml │ ├── vrf-lite-common.png │ ├── vrf-lite-simple.png │ └── vrf-route-leaking.yml └── vrf-lite-routers │ ├── README.md │ ├── multi-vrf.yml │ └── vrf-route-leaking.yml ├── VXLAN ├── vxlan-bridging │ ├── README.md │ ├── topology.yml │ └── vxlan-bridging.png ├── vxlan-ospf-evpn │ ├── README.md │ ├── src │ │ ├── arista-vxlan-e2e.png │ │ ├── arista-vxlan.png │ │ └── eos_vxlan.cfg │ └── topology.yml ├── vxlan-ospf │ ├── README.md │ ├── topology-broken.yml │ ├── topology-fixed.yml │ └── vxlan-ospf.png ├── vxlan-router-stick │ ├── README.md │ ├── netlab-vxlan-router-stick.png │ └── topology.yml └── vxlan-vrf-lite │ ├── README.md │ ├── netlab-vxlan-vrf-lite.png │ └── topology.yml ├── WAN └── wemulate │ └── topology.yml ├── config ├── eos-bash │ ├── README.md │ ├── run-script │ │ ├── deploy.eos-clab.yml │ │ ├── deploy.eos.yml │ │ └── eos.j2 │ └── topology.yml └── simple │ ├── README.md │ ├── bgp.png │ └── topology.yml ├── graphs └── colors-lines │ ├── README.md │ ├── baseline.png │ ├── link-width.png │ ├── link-width.yml │ ├── spine-color.png │ ├── spine-color.yml │ ├── text-color.png │ ├── text-color.yml │ └── topology.yml ├── multi-platform ├── asav │ ├── README.md │ ├── acl.j2 │ ├── config │ │ ├── ext-daemons │ │ ├── ext.cfg │ │ ├── fw.cfg │ │ ├── int-daemons │ │ └── int.cfg │ ├── graph.d2 │ ├── graph.png │ └── topology.yml ├── bgp-anycast │ ├── README.md │ ├── bgp-addpath │ │ ├── eos.j2 │ │ └── ios.j2 │ ├── bgp-anycast │ │ ├── cumulus.j2 │ │ ├── eos.j2 │ │ └── ios.j2 │ ├── graph.dot │ ├── multi-platform-bgp-anycast.png │ └── topology.yml └── cyber-crane-mesh │ ├── README.md │ ├── img │ ├── cyber-crane-mesh.png │ └── cyber-crane-mesh.svg │ ├── playbooks │ ├── master_playbook.yml │ ├── security_playbooks.yml │ └── sub_playbooks │ │ ├── disable_lldp.yml │ │ ├── mgmt_protocols.yml │ │ ├── no_ip_host.yml │ │ ├── s3_eos_lag.yml │ │ ├── s4_model_multiagent.yml │ │ ├── s4_vrf_tenant_routes.yml │ │ ├── s5_igmp_groups.yml │ │ ├── s5_pim_interfaces.yml │ │ ├── save_config_changes.yml │ │ ├── srx_firewall.yml │ │ ├── srx_ipsec_d1xfw01.yml │ │ ├── srx_ipsec_d2xfw01.yml │ │ ├── srx_nat_d1xfw01.yml │ │ ├── srx_policies_d1xfw01.yml │ │ └── srx_policies_d2xfw01.yml │ └── topology.yml ├── multihoming ├── redundant-small-site │ ├── initial │ │ ├── gw_a.cfg │ │ └── gw_b.cfg │ ├── routing │ │ ├── gw_a.cfg │ │ └── gw_b.cfg │ └── topology.yml └── small-site │ ├── README.md │ ├── gw-basic-config.cfg │ ├── gw-dual-sla.cfg │ ├── gw-eem-applet.cfg │ ├── gw-local-policy.cfg │ ├── gw-reliable-static-routes.cfg │ ├── ifdown │ └── frr.j2 │ ├── ifup │ └── frr.j2 │ └── topology.yml ├── plugins ├── adjust-bgp-sessions │ ├── README.md │ ├── anycast.original.png │ ├── bgp-addpath │ │ └── ios.j2 │ ├── bgp-anycast │ │ ├── defaults.yml │ │ ├── ios.j2 │ │ └── plugin.py │ ├── graph.bgp.png │ ├── graph.dot │ └── topology.yml ├── adjust-config-template │ ├── bgp-addpath │ │ ├── ios.j2 │ │ └── plugin.py │ ├── bgp-anycast │ │ ├── ios.j2 │ │ └── plugin.py │ └── topology.yml └── leaf-spine-fabric │ ├── README.md │ ├── leaf-spine-hosts.png │ └── topology.yml ├── routing ├── anycast-bgp-addpath │ ├── README.md │ ├── bgp-addpath.j2 │ ├── bgp-anycast.j2 │ ├── graph.bgp.png │ ├── graph.dot │ ├── ios-config-addpath-external-next-hop.tgz │ ├── ios-config-addpath.tgz │ ├── topology-external-next-hop.yml │ └── topology.yml ├── anycast-gateway │ ├── README.md │ ├── anycast-gateway.png │ └── topology.yml ├── anycast-mpls-ospf │ ├── MPLS-anycast-ospf-topo.png │ ├── README.md │ ├── anycast-mpls-ospf.tar.gz │ ├── ospf-anycast-loopback.j2 │ └── topology.yml ├── anycast-mpls │ ├── bgp-addpath │ │ ├── ios.j2 │ │ └── plugin.py │ ├── bgp-anycast │ │ ├── ios.j2 │ │ └── plugin.py │ ├── bgp-lu.j2 │ ├── graph.bgp.png │ ├── graph.ospf.png │ ├── mpls-ldp-bgp.j2 │ ├── ospf+bgp-external-next-hop.yml │ ├── ospf+bgp-initial.yml │ ├── ospf+bgp.yml │ ├── ospf-anycast-loopback.j2 │ └── ospf.yml ├── arp │ ├── arp-static.png │ ├── graph.d2 │ └── topology.yml ├── failover │ ├── README.md │ ├── Vagrantfile │ ├── ansible.cfg │ ├── group_vars │ │ ├── csr │ │ │ └── topology.yml │ │ └── iosv │ │ │ └── topology.yml │ ├── host_vars │ │ ├── p1 │ │ │ └── topology.yml │ │ ├── p2 │ │ │ └── topology.yml │ │ ├── pe1 │ │ │ └── topology.yml │ │ └── pe2 │ │ │ └── topology.yml │ ├── hosts.yml │ ├── igp-failover-topology.png │ ├── routing-bgp-fallover.j2 │ ├── routing-bgp.j2 │ ├── routing-ospf.eos.j2 │ ├── routing-ospf.j2 │ ├── routing-ospf.nxos.j2 │ └── topology.yml ├── gre-unnumbered │ ├── Vagrantfile │ ├── ansible.cfg │ ├── bgp │ │ ├── r1.cfg │ │ └── r2.cfg │ ├── group_vars │ │ └── csr │ │ │ └── topology.yml │ ├── host_vars │ │ ├── r1 │ │ │ └── topology.yml │ │ └── r2 │ │ │ └── topology.yml │ ├── hosts.yml │ ├── ospf │ │ ├── r1.cfg │ │ └── r2.cfg │ ├── static-route │ │ ├── r1.cfg │ │ └── r2.cfg │ └── topology.yml ├── lan-unnumbered │ ├── .gitignore │ ├── Addr-Ethernet-Unnumbered-DHCP.png │ ├── DHCP │ │ ├── c1.cfg │ │ ├── c2.cfg │ │ └── rtr.cfg │ ├── README.md │ ├── dhcp-client.j2 │ ├── dhcp-server.j2 │ ├── dhcp.yml │ ├── static-routes │ │ ├── c1.cfg │ │ ├── c2.cfg │ │ └── rtr.cfg │ └── topology.yml ├── multi-as │ ├── README.md │ └── topology.yml ├── ospf-unnumbered │ ├── graph.d2 │ ├── ospf-unnumbered.png │ ├── parallel.yml │ └── topology.yml ├── rsvp-mpls-vsrx │ ├── README.md │ ├── junos_mpls_rsvp.j2 │ ├── mpls.drawio │ ├── mpls.png │ └── topology.yml ├── sr-isis-te-vsrx │ ├── README.md │ ├── mpls.drawio │ ├── mpls.png │ └── topology.yml ├── sr-isis-te │ ├── README.md │ ├── epipe.py │ ├── macsec.j2 │ ├── mpls-te-path.j2 │ ├── mpls-te.py │ ├── sdp-epipe.j2 │ └── topology.yml ├── sr-mpls-bgp-srlinux │ ├── CLab_MPLS_SR.PNG │ ├── README.md │ └── topology.yml ├── sr-mpls-bgp │ ├── README.md │ ├── bgp.png │ ├── config │ │ ├── c1.initial.cfg │ │ ├── c1.isis.cfg │ │ ├── c1.sr.cfg │ │ ├── c2.initial.cfg │ │ ├── c2.isis.cfg │ │ ├── c2.sr.cfg │ │ ├── e1.bgp.cfg │ │ ├── e1.initial.cfg │ │ ├── e1.isis.cfg │ │ ├── e1.sr.cfg │ │ ├── e2.bgp.cfg │ │ ├── e2.initial.cfg │ │ ├── e2.isis.cfg │ │ ├── e2.sr.cfg │ │ ├── x1.bgp.cfg │ │ ├── x1.initial.cfg │ │ ├── x2.bgp.cfg │ │ └── x2.initial.cfg │ ├── graph.dot │ ├── hosts.yml │ ├── topology.png │ └── topology.yml ├── sr-mpls-sid │ ├── Vagrantfile │ ├── ansible.cfg │ ├── config │ │ ├── c1.initial.cfg │ │ ├── c1.isis.cfg │ │ ├── c1.sr.cfg │ │ ├── c2.initial.cfg │ │ ├── c2.isis.cfg │ │ ├── c2.sr.cfg │ │ ├── e1.initial.cfg │ │ ├── e1.isis.cfg │ │ ├── e1.sr.cfg │ │ ├── e2.initial.cfg │ │ ├── e2.isis.cfg │ │ └── e2.sr.cfg │ ├── group_vars │ │ ├── csr │ │ │ └── topology.yml │ │ └── eos │ │ │ └── topology.yml │ ├── host_vars │ │ ├── c1 │ │ │ └── topology.yml │ │ ├── c2 │ │ │ └── topology.yml │ │ ├── e1 │ │ │ └── topology.yml │ │ └── e2 │ │ │ └── topology.yml │ ├── hosts.yml │ └── topology.yml └── unnumbered │ ├── Vagrantfile │ ├── ansible.cfg │ ├── config.log │ ├── config │ ├── a_eos.cfg │ ├── c_csr.cfg │ ├── c_nxos.cfg │ └── j_vsrx.cfg │ ├── group_vars │ ├── all │ │ └── topology.yml │ ├── csr │ │ └── topology.yml │ ├── eos │ │ └── topology.yml │ ├── nxos │ │ └── topology.yml │ └── vsrx │ │ └── topology.yml │ ├── host_vars │ ├── a_eos │ │ └── topology.yml │ ├── c_csr │ │ └── topology.yml │ ├── c_nxos │ │ └── topology.yml │ └── j_vsrx │ │ └── topology.yml │ ├── hosts.yml │ └── topology.yml ├── services └── syslog │ ├── README.md │ ├── topology.png │ └── topology.yml ├── setup.sh ├── timing ├── README.md ├── timing.sh └── topology.yml └── topology ├── LAN └── topology.yml └── PVLAN ├── Vagrantfile └── isolate.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "ghcr.io/ipspace/netlab/devcontainer:latest", 3 | "hostRequirements": { 4 | "cpus": 2, 5 | "memory": "8gb", 6 | "storage": "32gb" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Vagrant directories 2 | .vagrant 3 | .DS_Store 4 | 5 | # Ignore netsim-tools files 6 | host_vars/ 7 | group_vars/ 8 | config/ 9 | hosts.yml 10 | Vagrantfile 11 | ansible.cfg 12 | 13 | # Ignore other Git repositories 14 | cicd/ 15 | # Ignore text files 16 | !requirements.txt 17 | *.txt 18 | 19 | # Ignore everything that should be ignored ;) 20 | *.ignore.* 21 | 22 | # Ignore Ansible retry and log files 23 | *.retry 24 | ansible.log 25 | 26 | # Ignore Python cache 27 | __pycache__/ 28 | 29 | # Ignore Visual Studio Code files 30 | .vscode 31 | 32 | # Don't publish SFTP configuration 33 | sftp-config.json 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools"] 2 | path = tools 3 | url = git@github.com:ipspace/netsim-tools.git 4 | -------------------------------------------------------------------------------- /Ansible/XML/filter_plugins/make_list.py: -------------------------------------------------------------------------------- 1 | # 2 | # Turn a dictionary (or most everything else) into a single-element list 3 | # 4 | from __future__ import (absolute_import, division, print_function) 5 | __metaclass__ = type 6 | 7 | from jinja2 import TemplateError 8 | 9 | def make_list(l): 10 | return l if type(l) is list else [l] 11 | 12 | class FilterModule(object): 13 | def filters(self): 14 | return { 15 | 'make_list': make_list 16 | } 17 | -------------------------------------------------------------------------------- /Ansible/XML/get-junos.yml: -------------------------------------------------------------------------------- 1 | - hosts: j_vsrx 2 | gather_facts: false 3 | tasks: 4 | - junipernetworks.junos.junos_command: 5 | commands: show route 6 | display: xml 7 | register: routes 8 | - debug: 9 | msg: | 10 | {% for table in routes.output[0]['rpc-reply']['route-information']['route-table'] %} 11 | t: {{ table['table-name']}} 12 | {% for entry in [table.rt]|flatten(levels=1) %} 13 | d: {{ entry['rt-destination'] }} 14 | {% endfor %} 15 | {% endfor %} 16 | - debug: 17 | msg: | 18 | {% for table in routes.output[0]['rpc-reply']['route-information']['route-table']|make_list %} 19 | t: {{ table['table-name']}} 20 | {% for entry in table.rt|make_list %} 21 | d: {{ entry['rt-destination'] }} 22 | {% endfor %} 23 | {% endfor %} 24 | -------------------------------------------------------------------------------- /Ansible/XML/get-nxos.yml: -------------------------------------------------------------------------------- 1 | - hosts: c_nxos 2 | gather_facts: false 3 | tasks: 4 | - cisco.nxos.nxos_command: 5 | commands: "show vlan | json" 6 | register: vlans 7 | - debug: 8 | msg: | 9 | {% for vlan in 10 | [ vlans.stdout[0].TABLE_vlanbrief.ROW_vlanbrief ] 11 | |flatten(levels=1) %} 12 | id: {{ vlan['vlanshowbr-vlanid'] }} name: {{ vlan['vlanshowbr-vlanname'] }} 13 | {% endfor %} 14 | - debug: 15 | msg: | 16 | {% for vlan in 17 | vlans.stdout[0].TABLE_vlanbrief.ROW_vlanbrief 18 | |make_list %} 19 | id: {{ vlan['vlanshowbr-vlanid'] }} name: {{ vlan['vlanshowbr-vlanname'] }} 20 | {% endfor %} 21 | -------------------------------------------------------------------------------- /Ansible/XML/topology.yml: -------------------------------------------------------------------------------- 1 | nodes: 2 | - name: c_nxos 3 | device: nxos 4 | - name: j_vsrx 5 | device: vsrx 6 | 7 | links: 8 | - c_nxos: 9 | j_vsrx: 10 | -------------------------------------------------------------------------------- /BGP/AS-prepend/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | gathering=explicit 3 | retry_files_enabled=false 4 | roles_path=. 5 | forks=10 6 | interpreter_python=auto_silent 7 | inventory=hosts-bgp-as.yml 8 | 9 | [persistent_connection] 10 | command_timeout=60 11 | 12 | [paramiko_connection] 13 | host_key_auto_add=no 14 | host_key_checking=no 15 | -------------------------------------------------------------------------------- /BGP/AS-prepend/bgp-as-path.j2: -------------------------------------------------------------------------------- 1 | ! 2 | router bgp {{ as }} 3 | network 192.168.0.{{ id }} mask 255.255.255.255 4 | {% for n in bgp %} 5 | {% set ndata = hostvars[n] %} 6 | {% set n_ip = "10.0.0."+(ndata.id|string()) %} 7 | neighbor {{ n_ip }} remote-as {{ ndata.as }} 8 | neighbor {{ n_ip }} description BGP sesssion to {{ n }} 9 | neighbor {{ n_ip }} next-hop-self 10 | {% endfor %} -------------------------------------------------------------------------------- /BGP/AS-prepend/initial-config.j2: -------------------------------------------------------------------------------- 1 | hostname {{ inventory_hostname }} 2 | ! 3 | no ip domain-lookup 4 | ! 5 | lldp run 6 | ! 7 | interface Loopback0 8 | ip address 192.168.0.{{ id }} 255.255.255.255 9 | ! 10 | interface GigabitEthernet0/0 11 | no lldp transmit 12 | no lldp receive 13 | ! 14 | interface GigabitEthernet0/1 15 | no shutdown 16 | ip address 10.0.0.{{ id }} 255.255.255.0 17 | ! 18 | no banner exec 19 | no banner login 20 | no banner incoming 21 | -------------------------------------------------------------------------------- /BGP/DMZ-BW/.gitignore: -------------------------------------------------------------------------------- 1 | host_vars/ 2 | group_vars/ 3 | hosts.yml 4 | ansible.cfg 5 | Vagrantfile 6 | -------------------------------------------------------------------------------- /BGP/DMZ-BW/BGP-Link-Bandwidth-Lab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/DMZ-BW/BGP-Link-Bandwidth-Lab.png -------------------------------------------------------------------------------- /BGP/DMZ-BW/core-bgp.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | address-family ipv4 3 | bgp dmzlink-bw 4 | maximum-paths 8 5 | maximum-paths ibgp 8 6 | -------------------------------------------------------------------------------- /BGP/DMZ-BW/edge-bgp.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | address-family ipv4 3 | bgp dmzlink-bw 4 | maximum-paths 8 5 | maximum-paths ibgp 8 6 | {% for n in bgp.neighbors if n.ipv4 is defined and n.type == 'ebgp' %} 7 | neighbor {{ n.ipv4 }} dmzlink-bw 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /BGP/DMZ-BW/lab.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Two-AS lab used to demonstrate how DMZ-BW works 3 | # 4 | 5 | defaults: 6 | device: csr 7 | 8 | module: [ bgp, ospf ] 9 | 10 | bgp: 11 | advertise_loopback: False 12 | as_list: 13 | 65000: 14 | members: [ e1, e2, pe1 ] 15 | rr: [ pe1 ] 16 | 65100: 17 | members: [ x1, x2 ] 18 | 19 | nodes: 20 | - e1 21 | - e2 22 | - pe1 23 | - name: x1 24 | device: iosv 25 | - name: x2 26 | device: iosv 27 | 28 | links: 29 | - pe1-e1 30 | - pe1-e2 31 | - e1: 32 | x1: 33 | bandwidth: 2000 34 | - e1: 35 | x2: 36 | bandwidth: 3000 37 | - e2: 38 | x2: 39 | bandwidth: 1000 40 | - x1: 41 | x2: 42 | bgp: 43 | advertise: True 44 | prefix: 45 | ipv4: 10.42.42.0/24 46 | -------------------------------------------------------------------------------- /BGP/Default-NH/ospf-default/eos.j2: -------------------------------------------------------------------------------- 1 | router ospf 1 2 | default-information originate always 3 | -------------------------------------------------------------------------------- /BGP/Default-NH/ospf-default/frr.j2: -------------------------------------------------------------------------------- 1 | router ospf 2 | default-information originate always 3 | -------------------------------------------------------------------------------- /BGP/Default-NH/topology.yml: -------------------------------------------------------------------------------- 1 | module: [ bgp, ospf ] 2 | 3 | plugin: [ bgp.session ] 4 | 5 | nodes: 6 | xt: 7 | bgp.as: 65100 8 | device: frr 9 | provider: clab 10 | edge: 11 | bgp.as: 65200 12 | bgp.next_hop_self: False 13 | config: [ ospf-default ] 14 | device: frr 15 | provider: clab 16 | int: 17 | bgp.as: 65200 18 | 19 | links: 20 | - xt: 21 | edge: 22 | - edge: 23 | int: 24 | mtu: 1500 25 | -------------------------------------------------------------------------------- /BGP/EBGP-policy/8212-default/frr.j2: -------------------------------------------------------------------------------- 1 | frr defaults traditional 2 | ! 3 | no router bgp {{ bgp.as }} 4 | ! 5 | router bgp {{ bgp.as }} 6 | {% for af in ['ipv4'] %} 7 | {% for n in bgp.neighbors if n[af] is defined %} 8 | neighbor {{ n[af] }} remote-as {{ n.as }} 9 | neighbor {{ n[af] }} description {{ n.name }} 10 | {% endfor %} 11 | {% if loopback[af] is defined and bgp.advertise_loopback %} 12 | network {{ loopback[af]|ipaddr(0) }} 13 | {% endif %} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /BGP/EBGP-policy/README.md: -------------------------------------------------------------------------------- 1 | # Enforcing Default EBGP Policy (RFC 8212) 2 | 3 | This directory contains *netlab* topology file that illustrates how FRR with defaults set to *traditional* enforces routing policies on EBGP sessions. See [Default EBGP Policy (RFC 8212)](https://blog.ipspace.net/2023/06/default-ebgp-policy-rfc-8212.html) blog post for more details. 4 | 5 | ![EBGP topology](ebgp-policy.png) 6 | 7 | ## Changing Device Types 8 | 9 | To test other devices supported by netlab, set the device type of the *customer* node with CLI parameters. You might also change the virtualization provider to *libvirt* or *virtualbox* (the topology file uses *containerlab*). 10 | 11 | For example, use the following to start the lab with Cisco IOSv as the customer router: 12 | 13 | ``` 14 | netlab up -p libvirt -s nodes.customer.device=iosv 15 | ``` 16 | -------------------------------------------------------------------------------- /BGP/EBGP-policy/ebgp-policy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/EBGP-policy/ebgp-policy.png -------------------------------------------------------------------------------- /BGP/EBGP-policy/topology.yml: -------------------------------------------------------------------------------- 1 | --- 2 | message: | 3 | This lab demonstrates the "hairpin leak" through a customer AS and the 4 | impact of "default EBGP policy" (RFC 8212) behavior of FRR. We have to 5 | use custom config on FRR because the default netlab configuration 6 | templates disables RFC 8212 functionality on FRR. 7 | 8 | provider: clab 9 | module: [ bgp ] 10 | defaults.device: eos 11 | 12 | nodes: 13 | customer: 14 | bgp.as: 65100 15 | config: [ "8212-default" ] 16 | device: frr 17 | isp_a: 18 | bgp.as: 65001 19 | isp_b: 20 | bgp.as: 65002 21 | 22 | links: [ customer-isp_a, customer-isp_b ] -------------------------------------------------------------------------------- /BGP/IBGP-local-AS/initial/x1.cfg: -------------------------------------------------------------------------------- 1 | frr version 10.2.2_git 2 | frr defaults datacenter 3 | hostname x1 4 | no ipv6 forwarding 5 | service integrated-vtysh-config 6 | ! 7 | vrf mgmt 8 | exit-vrf 9 | ! 10 | interface eth1 11 | description x1 -> dut [external] 12 | ip address 10.1.0.2/30 13 | exit 14 | ! 15 | interface lo 16 | ip address 172.42.1.1/24 17 | exit 18 | ! 19 | router bgp 65100 20 | bgp router-id 172.42.1.1 21 | no bgp default ipv4-unicast 22 | bgp bestpath as-path multipath-relax 23 | neighbor 10.1.0.1 remote-as 65002 24 | neighbor 10.1.0.1 description dut 25 | ! 26 | address-family ipv4 unicast 27 | network 172.42.1.0/24 28 | neighbor 10.1.0.1 activate 29 | no neighbor 10.1.0.1 send-community extended 30 | exit-address-family 31 | exit 32 | ! 33 | -------------------------------------------------------------------------------- /BGP/IBGP-local-AS/initial/x2.cfg: -------------------------------------------------------------------------------- 1 | frr version 10.2.2_git 2 | frr defaults datacenter 3 | hostname x2 4 | no ipv6 forwarding 5 | service integrated-vtysh-config 6 | ! 7 | vrf mgmt 8 | exit-vrf 9 | ! 10 | interface eth1 11 | description x2 -> dut [external] 12 | ip address 10.1.0.6/30 13 | exit 14 | ! 15 | interface lo 16 | ip address 172.42.2.1/24 17 | exit 18 | ! 19 | router bgp 65101 20 | bgp router-id 172.42.2.1 21 | no bgp default ipv4-unicast 22 | bgp bestpath as-path multipath-relax 23 | neighbor 10.1.0.5 remote-as 65101 24 | neighbor 10.1.0.5 description dut 25 | ! 26 | address-family ipv4 unicast 27 | network 172.42.2.0/24 28 | neighbor 10.1.0.5 activate 29 | neighbor 10.1.0.5 next-hop-self 30 | exit-address-family 31 | exit 32 | ! 33 | -------------------------------------------------------------------------------- /BGP/IGP-metric/topology-2igp.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | device: iosv 3 | 4 | module: [ eigrp, ospf, bgp ] 5 | 6 | bgp: 7 | as: 65000 8 | advertise_loopback: False 9 | 10 | ospf: 11 | reference_bandwidth: 10000 12 | 13 | nodes: 14 | e1: 15 | bgp: 16 | originate: [ 172.16.0.0/16 ] 17 | e2: 18 | module: [ ospf, bgp ] 19 | bgp: 20 | originate: [ 172.16.0.0/16 ] 21 | pe1: 22 | 23 | links: 24 | - pe1: 25 | e1: 26 | ospf.cost: 1 27 | - pe1: 28 | e2: 29 | bandwidth: 100000 30 | eigrp: False 31 | -------------------------------------------------------------------------------- /BGP/IGP-metric/topology-isis.yml: -------------------------------------------------------------------------------- 1 | defaults.device: eos 2 | 3 | module: [ isis, ospf, bgp ] 4 | 5 | bgp: 6 | as: 65000 7 | advertise_loopback: False 8 | 9 | nodes: 10 | e1: 11 | bgp: 12 | originate: [ 172.16.0.0/16 ] 13 | e2: 14 | module: [ isis, bgp ] 15 | bgp: 16 | originate: [ 172.16.0.0/16 ] 17 | pe1: 18 | 19 | # 20 | # The lab topology uses static OSPF and ISIS costs because Arista vEOS cannot 21 | # set bandwidth of its Ethernet interfaces 22 | # 23 | links: 24 | - pe1: 25 | e1: 26 | bandwidth: 1000 27 | ospf.cost: 1000 28 | isis.cost: 10 29 | 30 | - pe1: 31 | e2: 32 | bandwidth: 100 33 | ospf: False 34 | isis.cost: 100 35 | -------------------------------------------------------------------------------- /BGP/IGP-metric/topology-ospf.yml: -------------------------------------------------------------------------------- 1 | defaults.device: iosv 2 | 3 | bgp: 4 | as: 65000 5 | advertise_loopback: False 6 | 7 | ospf: 8 | reference_bandwidth: 100000 9 | 10 | module: [ ospf, bgp ] 11 | 12 | nodes: 13 | e1: 14 | bgp: 15 | originate: [ 172.16.0.0/16 ] 16 | e2: 17 | bgp: 18 | originate: [ 172.16.0.0/16 ] 19 | pe1: 20 | 21 | links: 22 | - pe1: 23 | e1: 24 | - pe1: 25 | e2: 26 | ospf.cost: 100 27 | bandwidth: 1000 28 | -------------------------------------------------------------------------------- /BGP/LocPref-Prepend/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | gathering=explicit 3 | retry_files_enabled=false 4 | roles_path=. 5 | forks=10 6 | interpreter_python=auto_silent 7 | inventory=hosts.yml 8 | stdout_callback=yaml 9 | host_key_checking=False 10 | ssh_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" 11 | 12 | [persistent_connection] 13 | command_timeout=60 14 | 15 | [paramiko_connection] 16 | host_key_auto_add=no 17 | host_key_checking=no 18 | -------------------------------------------------------------------------------- /BGP/LocPref-Prepend/group_vars/frr/topology.yml: -------------------------------------------------------------------------------- 1 | # Ansible inventory created from ['topology.yml', 'package:topology-defaults.yml'] 2 | # 3 | --- 4 | ansible_connection: docker 5 | ansible_network_os: frr 6 | -------------------------------------------------------------------------------- /BGP/LocPref-Prepend/hosts.yml: -------------------------------------------------------------------------------- 1 | # Ansible inventory created from ['topology.yml', 'package:topology-defaults.yml'] 2 | # 3 | --- 4 | frr: 5 | hosts: 6 | c1: 7 | ansible_host: clab-LocPref-Prepend-c1 8 | id: 1 9 | c2: 10 | ansible_host: clab-LocPref-Prepend-c2 11 | id: 2 12 | pe1: 13 | ansible_host: clab-LocPref-Prepend-pe1 14 | id: 4 15 | pe2: 16 | ansible_host: clab-LocPref-Prepend-pe2 17 | id: 5 18 | pe3: 19 | ansible_host: clab-LocPref-Prepend-pe3 20 | id: 6 21 | x1: 22 | ansible_host: clab-LocPref-Prepend-x1 23 | id: 3 24 | -------------------------------------------------------------------------------- /BGP/LocPref-Prepend/topology.yml: -------------------------------------------------------------------------------- 1 | provider: clab 2 | defaults.device: frr 3 | module: [ bgp, ospf ] 4 | 5 | bgp: 6 | as_list: 7 | 65000: 8 | members: [ pe1, pe2, pe3, p, rr ] 9 | rr: [ rr ] 10 | name: provider 11 | 65001: 12 | members: [ x1 ] 13 | 65100: 14 | members: [ c1 ] 15 | name: customer 16 | 65101: 17 | members: [ c2 ] 18 | name: remote 19 | 20 | nodes: [ c1, c2, x1, pe1, pe2, pe3, rr, p ] 21 | 22 | links: 23 | - c1-pe1 24 | - c2-pe2 25 | - c1-x1 26 | - x1-pe3 27 | - pe1-p 28 | - pe2-p 29 | - pe3-p 30 | - rr-p 31 | -------------------------------------------------------------------------------- /BGP/LocalAS-Migration/README.md: -------------------------------------------------------------------------------- 1 | # Network Migration with BGP Local-AS Feature 2 | 3 | This directory contains the _[netlab](https://netlab.tools/)_ topologies and device configurations described in the [Network Migration with BGP Local-AS Feature](https://blog.ipspace.net/2009/03/bgp-local-as-feature-basics/) blog post: 4 | 5 | * Initial [netlab topology](topology.yml) and [device configurations](initial) 6 | * Post-migration [netlab topology](migrated.yml) and [device configurations](migrated) 7 | 8 | ![](https://blog.ipspace.net/2009/03/localas-initial.png) -------------------------------------------------------------------------------- /BGP/LocalAS-Migration/migrated.yml: -------------------------------------------------------------------------------- 1 | defaults.device: iol # Change to your IOS device (ex: iosv, csr, cat8000v) 2 | provider: clab # Change to 'libvirt' if needed 3 | 4 | module: [ bgp ] 5 | bgp.advertise_loopback: False 6 | bgp.replace_global_as: False 7 | 8 | groups: 9 | isp: 10 | members: [ r1, r2, ispa ] 11 | module: [ bgp, ospf ] 12 | bgp.as: 64500 13 | 14 | nodes: 15 | ispa: 16 | bgp.originate: 10.6.6.0/24 17 | r1: 18 | bgp.rr: True 19 | r2: 20 | custa: 21 | bgp.as: 65000 22 | bgp.originate: 10.8.8.0/24 23 | custb: 24 | bgp.as: 65100 25 | bgp.originate: 10.9.9.0/24 26 | 27 | links: 28 | - ispa-r1 29 | - r1-r2 30 | - r1: 31 | bgp.local_as: 64510 32 | custa: 33 | - r2: 34 | bgp.local_as: 64510 35 | custb: 36 | -------------------------------------------------------------------------------- /BGP/LocalAS-Migration/topology.yml: -------------------------------------------------------------------------------- 1 | defaults.device: iol # Change to your IOS device (ex: iosv, csr, cat8000v) 2 | provider: clab # Change to 'libvirt' if needed 3 | 4 | module: [ bgp ] 5 | bgp.advertise_loopback: False 6 | 7 | groups: 8 | isp_b: 9 | members: [ r1, r2 ] 10 | module: [ bgp, ospf ] 11 | bgp.as: 64510 12 | 13 | nodes: 14 | ispa: 15 | bgp.as: 64500 16 | bgp.originate: 10.6.6.0/24 17 | r1: 18 | r2: 19 | custa: 20 | bgp.as: 65000 21 | bgp.originate: 10.8.8.0/24 22 | custb: 23 | bgp.as: 65100 24 | bgp.originate: 10.9.9.0/24 25 | 26 | links: [ ispa-r1, r1-r2, r1-custa, r2-custb ] 27 | -------------------------------------------------------------------------------- /BGP/Multi-Loopback/bgp-ipv6.eos.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | {% for n in bgp.neighbors %} 3 | {% set rid = hostvars[n.name].id %} 4 | neighbor 10.42.42.{{ rid }} remote-as 65000 5 | neighbor 10.42.42.{{ rid }} update-source loopback42 6 | {% endfor %} 7 | address-family ipv6 8 | {% for n in bgp.neighbors %} 9 | {% set rid = hostvars[n.name].id %} 10 | neighbor 10.42.42.{{ rid }} activate 11 | {% endfor %} -------------------------------------------------------------------------------- /BGP/Multi-Loopback/bgp-ipv6.ios.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | {% for n in bgp.neighbors %} 3 | {% set rid = hostvars[n.name].id %} 4 | neighbor 10.42.42.{{ rid }} remote-as 65000 5 | neighbor 10.42.42.{{ rid }} update-source loopback42 6 | {% endfor %} 7 | address-family ipv6 unicast 8 | {% for n in bgp.neighbors %} 9 | {% set rid = hostvars[n.name].id %} 10 | neighbor 10.42.42.{{ rid }} activate 11 | {% endfor %} -------------------------------------------------------------------------------- /BGP/Multi-Loopback/bgp-ipv6.nxos.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | {% for n in bgp.neighbors %} 3 | {% set rid = hostvars[n.name].id %} 4 | neighbor 10.42.42.{{ rid }} 5 | remote-as 65000 6 | update-source loopback42 7 | address-family ipv6 unicast 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /BGP/Multi-Loopback/loopback.eos.j2: -------------------------------------------------------------------------------- 1 | interface loopback 42 2 | ip address 10.42.42.{{ id }}/32 3 | ip ospf area 0.0.0.0 4 | 5 | -------------------------------------------------------------------------------- /BGP/Multi-Loopback/loopback.ios.j2: -------------------------------------------------------------------------------- 1 | interface loopback 42 2 | ip address 10.42.42.{{ id }} 255.255.255.255 3 | ip ospf 1 area 0 4 | -------------------------------------------------------------------------------- /BGP/Multi-Loopback/loopback.nxos.j2: -------------------------------------------------------------------------------- 1 | interface loopback 42 2 | ip address 10.42.42.{{ id }}/32 3 | ip router ospf 1 area 0.0.0.0 4 | -------------------------------------------------------------------------------- /BGP/Multi-Loopback/topology.yml: -------------------------------------------------------------------------------- 1 | module: [ bgp,ospf ] 2 | 3 | bgp.as: 65000 4 | 5 | groups: 6 | all: 7 | config: [ loopback, bgp-ipv6 ] 8 | 9 | nodes: 10 | r1: 11 | device: iosv 12 | r2: 13 | device: iosv 14 | s1: 15 | device: nxos 16 | s2: 17 | device: eos 18 | 19 | links: [ r1-r2, r2-s1, s1-s2, s2-r1 ] 20 | -------------------------------------------------------------------------------- /BGP/Multipath/.gitignore: -------------------------------------------------------------------------------- 1 | graph.* 2 | hosts.* 3 | host_vars/ 4 | group_vars/ 5 | ansible.cfg 6 | Vagrantfile 7 | -------------------------------------------------------------------------------- /BGP/Multipath/baseline.yml: -------------------------------------------------------------------------------- 1 | provider: clab 2 | module: [ bgp, ospf ] 3 | defaults.device: eos 4 | bgp.as: 65000 5 | 6 | nodes: 7 | a: 8 | b: 9 | c: 10 | d: 11 | rr: 12 | bgp.rr: True 13 | id: 1 14 | y: 15 | bgp.as: 65100 16 | bgp.originate: [ 10.42.42.0/24 ] 17 | 18 | links: [ a-b, a-c, b-d, c-d, b-rr, d-rr, c-y, d-y ] 19 | -------------------------------------------------------------------------------- /BGP/Multipath/bgp-addpath.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | address-family ipv4 unicast 3 | {% if bgp.rr is defined %} 4 | bgp additional-paths select all 5 | bgp additional-paths send 6 | {% else %} 7 | maximum-paths 16 8 | maximum-paths ibgp 16 9 | {% endif %} 10 | {% for n in bgp.neighbors if n.type == 'ibgp' %} 11 | {% if bgp.rr is defined %} 12 | neighbor {{ n.ipv4 }} advertise additional-paths all 13 | {% else %} 14 | neighbor {{ n.ipv4 }} additional-paths receive 15 | {% endif %} 16 | {% endfor %} 17 | -------------------------------------------------------------------------------- /BGP/Multipath/test-loopback.j2: -------------------------------------------------------------------------------- 1 | ! Create a loopback interface to make BGP prefix pingable 2 | ! 3 | interface loopback 42 4 | ip address 10.42.42.42 255.255.255.255 5 | -------------------------------------------------------------------------------- /BGP/Multipath/topology.yml: -------------------------------------------------------------------------------- 1 | module: [ bgp, ospf ] 2 | defaults.device: iosv 3 | bgp.as: 65000 4 | 5 | groups: 6 | net: 7 | members: [ a,b,c,d,m,rr ] 8 | config: [ bgp-addpath.j2 ] 9 | ext: 10 | members: [ y ] 11 | config: [ test-loopback.j2 ] 12 | 13 | nodes: 14 | a: 15 | b: 16 | c: 17 | d: 18 | m: 19 | rr: 20 | bgp.rr: True 21 | id: 1 22 | y: 23 | bgp.as: 65100 24 | bgp.originate: [ 10.42.42.0/24 ] 25 | 26 | links: [ a-b, a-c, b-d, c-d, b-rr, d-rr, c-y, d-y, m-c, m-d ] 27 | -------------------------------------------------------------------------------- /BGP/Multipath_sros/Multipath_sros.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/Multipath_sros/Multipath_sros.PNG -------------------------------------------------------------------------------- /BGP/Multipath_sros/bgp-addpath/srlinux.j2: -------------------------------------------------------------------------------- 1 | updates: 2 | - path: network-instance[name=default]/protocols/bgp 3 | val: 4 | afi-safi: 5 | - afi-safi-name: ipv4-unicast 6 | add-paths: 7 | receive: True 8 | send: True 9 | send-multipath: [null] 10 | {% if bgp.rr|default(0)|bool %} 11 | # best-path-selection: # Need this, else D wins over C due to OSPF cost metric 12 | # ignore-nh-metric: True 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /BGP/Multipath_sros/bgp-addpath/sros.j2: -------------------------------------------------------------------------------- 1 | updates: 2 | - path: configure/router[router-name=Base]/bgp 3 | val: 4 | ibgp-multipath: True # Not strictly needed 5 | add-paths: 6 | ipv4: 7 | send: multipaths 8 | receive: True 9 | {% if bgp.rr|default(0)|bool %} 10 | best-path-selection: # Need this, else D wins over C due to OSPF cost metric 11 | ignore-nh-metric: True 12 | {% endif %} 13 | -------------------------------------------------------------------------------- /BGP/Multipath_sros/topology.yml: -------------------------------------------------------------------------------- 1 | module: [ bgp, ospf ] 2 | 3 | # JvB added 4 | provider: clab 5 | 6 | defaults.device: srlinux # iosv 7 | bgp.as: 65000 8 | 9 | groups: 10 | net: 11 | members: [ a,b,c,d,m,rr ] 12 | add_path: 13 | members: [ m, rr ] 14 | config: [ bgp-addpath ] 15 | ext: 16 | members: [ y ] 17 | # config: [ test-loopback.j2 ] # using 'generate-icmp true' in SRL 18 | 19 | nodes: 20 | a: 21 | b: 22 | c: 23 | d: 24 | m: # Starting with R23.3.1 SRL now supports ADD_PATH capability 25 | device: srlinux 26 | rr: 27 | device: sros 28 | bgp.rr: True 29 | id: 1 30 | y: 31 | bgp.as: 65100 32 | bgp.originate: [ 10.42.42.0/24 ] 33 | 34 | links: [ a-b, a-c, b-d, c-d, b-rr, d-rr, c-y, d-y, m-c, m-d ] 35 | -------------------------------------------------------------------------------- /BGP/RR-cluster-id/bgp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/RR-cluster-id/bgp.png -------------------------------------------------------------------------------- /BGP/RR-cluster-id/topology.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Using FRR on containerlab to make it fast 3 | # 4 | defaults.device: iosv 5 | # 6 | module: [ isis, bgp ] 7 | 8 | bgp.as: 65000 9 | bgp.rr_cluster_id: False 10 | 11 | nodes: 12 | rr1: 13 | bgp.rr: True 14 | rr2: 15 | bgp.rr: True 16 | pe1: 17 | pe2: 18 | 19 | links: [ rr1-pe1, rr1-pe2, rr2-pe1, rr2-pe2 ] 20 | -------------------------------------------------------------------------------- /BGP/RR-next-hop-self/BGP-RR-next-hop-self-topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/RR-next-hop-self/BGP-RR-next-hop-self-topology.png -------------------------------------------------------------------------------- /BGP/RR-next-hop-self/eos-rr-next-hop-self-fixed.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/RR-next-hop-self/eos-rr-next-hop-self-fixed.tar.gz -------------------------------------------------------------------------------- /BGP/RR-next-hop-self/eos-rr-next-hop-self.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/RR-next-hop-self/eos-rr-next-hop-self.tar.gz -------------------------------------------------------------------------------- /BGP/RR-next-hop-self/topology.yml: -------------------------------------------------------------------------------- 1 | --- 2 | defaults.device: eos 3 | 4 | module: [ bgp, ospf ] 5 | bgp.as: 65000 6 | 7 | nodes: 8 | rr: 9 | id: 1 10 | bgp.rr: true 11 | e1: 12 | e2: 13 | x1: 14 | bgp.as: 65100 15 | 16 | links: [ e1-rr, e2-rr, x1-rr ] 17 | -------------------------------------------------------------------------------- /BGP/RR-sessions/README.md: -------------------------------------------------------------------------------- 1 | # BGP Route Reflector with EBGP Session 2 | 3 | This lab illustrates the need for IBGP sessions between BGP route reflectors. The BGP topology in the lab is as follow (arrows indicate IBGP sessions between route reflectors and their clients): 4 | 5 | ![BGP topology](bgp.png) 6 | 7 | To set up this lab: 8 | 9 | * [Install netlab](https://netsim-tools.readthedocs.io/en/latest/install.html) on a Ubuntu machine with **pip3 install --upgrade networklab** 10 | * Install Docker and Containerlab on the same machine with **netlab install containerlab** 11 | * Copy topology.yml into an empty directory 12 | * Execute **netlab up** 13 | -------------------------------------------------------------------------------- /BGP/RR-sessions/bgp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/RR-sessions/bgp.png -------------------------------------------------------------------------------- /BGP/RR-sessions/topology.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Using FRR on containerlab to make it fast 3 | # 4 | defaults.device: frr 5 | provider: clab 6 | # 7 | module: [ isis, bgp ] 8 | 9 | bgp.as_list: 10 | 65000: 11 | members: [ rr1, rr2, pe1, pe2 ] 12 | rr: [ rr1, rr2 ] 13 | 65001: 14 | members: [ x1 ] 15 | 16 | groups: 17 | as65000: 18 | bgp.advertise_loopback: false 19 | 20 | nodes: [ rr1, rr2, pe1, pe2, x1 ] 21 | 22 | links: [ rr1-pe1, rr1-pe2, rr2-pe1, rr2-pe2, rr1-x1 ] 23 | -------------------------------------------------------------------------------- /BGP/Route-Leaks/leak-practice-lab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/Route-Leaks/leak-practice-lab.png -------------------------------------------------------------------------------- /BGP/Sessions/README.md: -------------------------------------------------------------------------------- 1 | # Testing BGP Sessions from Unknown Sources 2 | 3 | This directory contains lab topologies used to test implementations of BGP session security features: 4 | 5 | * `unknown-source-ip.yml` tests the device behavior when receiving a TCP SYN packet from an unknown IP address. The results are described in the _"[Will Network Devices Reject BGP Sessions from Unknown Sources?](https://blog.ipspace.net/2023/10/reject-unknown-bgp-session.html)"_ blog post. 6 | * `gtsm.yml` tests the behavior of a BGP speaker configured with TTL session protection (GTSM) when receiving TCP packets with too-low TTL. 7 | -------------------------------------------------------------------------------- /BGP/Sessions/gtsm.yml: -------------------------------------------------------------------------------- 1 | provider: libvirt 2 | 3 | plugin: [ bgp.session ] 4 | module: [ bgp ] 5 | 6 | nodes: 7 | dut: # Device under test, change with 'netlab up -d' 8 | bgp.as: 65000 9 | peer: # A valid peer -- FRR daemon running in a container 10 | bgp.as: 65001 11 | device: frr 12 | provider: clab 13 | 14 | links: 15 | - dut: 16 | bgp.gtsm: True 17 | bgp.passive: True 18 | peer: 19 | -------------------------------------------------------------------------------- /BGP/Sessions/nofake.py: -------------------------------------------------------------------------------- 1 | from box import Box 2 | 3 | def post_transform(topo: Box) -> None: 4 | for ndata in topo.nodes.values(): 5 | if not ndata.get('bgp.neighbors',[]): 6 | continue 7 | 8 | ndata.bgp.neighbors = [ ngb for ngb in ndata.bgp.neighbors if ngb['as'] != 65013 ] 9 | -------------------------------------------------------------------------------- /BGP/Sessions/router_id.yml: -------------------------------------------------------------------------------- 1 | module: [ bgp,ospf ] 2 | 3 | nodes: 4 | xt: 5 | bgp.as: 65100 6 | bgp.router_id: 10.42.42.42 7 | edge: 8 | bgp.as: 65200 9 | bgp.router_id: 10.42.42.42 10 | int: 11 | bgp.as: 65200 12 | bgp.router_id: 10.42.42.42 13 | 14 | links: [ xt-edge, edge-int ] -------------------------------------------------------------------------------- /BGP/Sessions/unknown-source-ip.yml: -------------------------------------------------------------------------------- 1 | defaults.device: eos 2 | 3 | module: [ bgp ] 4 | 5 | plugin: [ nofake ] # This plugin removes BGP neighbors in AS 65013 6 | # ... making the 'fake' device unknown to 'dut' 7 | nodes: 8 | dut: # Device under test, change with 'netlab up -d' 9 | bgp.as: 65000 10 | peer: # A valid peer -- FRR daemon running in a container 11 | bgp.as: 65001 12 | device: frr 13 | provider: clab 14 | fake: # An intruder -- FRR daemon running in a container 15 | bgp.as: 65013 16 | device: frr 17 | provider: clab 18 | id: 13 19 | 20 | links: 21 | - dut: 22 | peer: 23 | - dut: # Link type set to LAN on the intruder link to make it easier 24 | fake: # ... to find the interface to listen to with tcpdump 25 | type: lan 26 | -------------------------------------------------------------------------------- /BGP/UCMP-anycast/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | netlab create ospf+ibgp.yml 3 | sudo containerlab deploy -t clab.yml 4 | netlab config anycast -l '~a[1-4]' -v 5 | netlab config bw-ucmp -l '~[ls][1-4]' -v 6 | ``` 7 | -------------------------------------------------------------------------------- /BGP/UCMP-anycast/anycast.j2: -------------------------------------------------------------------------------- 1 | ! 2 | interface lo0 3 | ip address 10.42.0.1/32 4 | ! 5 | router bgp {{ bgp.as }} 6 | address-family ipv4 unicast 7 | network 10.42.0.1/32 8 | -------------------------------------------------------------------------------- /BGP/UCMP-anycast/bw-ucmp.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | bgp bestpath as-path multipath-relax 3 | bgp bestpath bandwidth default-weight-for-missing 4 | -------------------------------------------------------------------------------- /BGP/UCMP-anycast/dmz-bw.j2: -------------------------------------------------------------------------------- 1 | ip prefix-list anycast seq 10 permit 10.42.0.0/16 ge 32 2 | ! 3 | route-map anycast-bw permit 10 4 | match ip address prefix-list anycast 5 | set extcommunity bandwidth num-multipaths 6 | ! 7 | route-map anycast-bw permit 20 8 | ! 9 | router bgp {{ bgp.as }} 10 | bgp bestpath as-path multipath-relax 11 | bgp bestpath bandwidth default-weight-for-missing 12 | ! 13 | address-family ipv4 unicast 14 | {% for n in bgp.neighbors if n.type == 'ibgp' %} 15 | neighbor {{ n.ipv4 }} route-map anycast-bw out 16 | {% endfor %} 17 | -------------------------------------------------------------------------------- /BGP/UCMP-anycast/ospf+ibgp.yml: -------------------------------------------------------------------------------- 1 | provider: clab 2 | defaults: 3 | device: frr 4 | 5 | module: [ ospf, bgp ] 6 | bgp: 7 | as_list: 8 | 65000: 9 | members: [ l1, l2, l3, l4, s1, s2 ] 10 | rr: [ s1, s2 ] 11 | 65101: 12 | members: [ a1 ] 13 | 65102: 14 | members: [ a2 ] 15 | 65103: 16 | members: [ a3 ] 17 | 65104: 18 | members: [ a4 ] 19 | 20 | nodes: [ l1, l2, l3, l4, s1, s2, a1, a2, a3, a4 ] 21 | 22 | links: 23 | - l1-s1 24 | - l1-s2 25 | - l2-s1 26 | - l2-s2 27 | - l3-s1 28 | - l3-s2 29 | - l4-s1 30 | - l4-s2 31 | - a1-l1 32 | - a2-l1 33 | - a3-l1 34 | - a4-l2 35 | -------------------------------------------------------------------------------- /BGP/Unnumbered/README.md: -------------------------------------------------------------------------------- 1 | # Unnumbered BGP 2 | 3 | This directory contains *netlab* topology file for a two-router unnumbered BGP test lab. 4 | 5 | ![BGP unnumbered topology](graph.png) 6 | 7 | After starting the lab, explore the BGP sessions, BGP tables, and IP routing tables on R1 and R2. 8 | 9 | ## Changing Device Types 10 | 11 | This topology can be used with all network devices supporting unnumbered EBGP sessions, and all virtualization providers supported by *netlab*: 12 | 13 | * To change device type, use the `-d xxx` CLI argument 14 | * To change the virtualization provider, use `-p` CLI argument. 15 | 16 | For example, to start the lab with FRR containers, use: 17 | 18 | ``` 19 | netlab up -d frr -p clab 20 | ``` 21 | -------------------------------------------------------------------------------- /BGP/Unnumbered/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/BGP/Unnumbered/graph.png -------------------------------------------------------------------------------- /BGP/Unnumbered/topology.yml: -------------------------------------------------------------------------------- 1 | --- 2 | module: [ bgp ] 3 | defaults.device: cumulus 4 | provider: clab 5 | 6 | nodes: 7 | r1: 8 | bgp.as: 65001 9 | r2: 10 | bgp.as: 65002 11 | 12 | links: 13 | - r1: 14 | r2: 15 | unnumbered: True 16 | - r1: 17 | r2: 18 | unnumbered: True 19 | 20 | message: 21 | The "EBGP Unnumbered" lab is ready. Use "netlab connect" to connect to the lab 22 | devices and "netlab status" if you forgot their names. 23 | -------------------------------------------------------------------------------- /BGP/bgpipe/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | 4 | LABEL maintainer="Netlab project " 5 | LABEL description="bgpipe container" 6 | 7 | RUN apt-get update && \ 8 | apt-get install -y bash iputils-ping net-tools iproute2 wget jq && \ 9 | wget https://github.com/bgpfix/bgpipe/releases/download/v0.8.8/bgpipe-linux-amd64 -q -O /usr/local/bin/bgpipe && \ 10 | chmod a+x /usr/local/bin/bgpipe 11 | 12 | WORKDIR /root 13 | 14 | CMD /usr/bin/bash 15 | -------------------------------------------------------------------------------- /BGP/bgpipe/README.md: -------------------------------------------------------------------------------- 1 | # Using BGP Pipe 2 | 3 | This directory contains the *netlab* topology file for a lab with a BGP router and a Linux host running BGP Pipe. It also contains a Dockerfile to build a container with BGP Pipe. 4 | 5 | You'll find more details in [this blog post](https://blog.ipspace.net/2024/08/netlab-bgpipe/). 6 | 7 | ## Changing Device Types 8 | 9 | The lab topology uses FRRouting running as a container. It can be used with any device supported by the netlab BGP configuration module and *libvirt* or *clab* virtualization provider: 10 | 11 | * To change device type, use the `-d xxx` CLI argument 12 | * To change the virtualization provider, use `-p` CLI argument. 13 | 14 | For example, to start the lab with a Nexus OS virtual machine, use: 15 | 16 | ``` 17 | netlab up -d nxos -p libvirt 18 | ``` 19 | -------------------------------------------------------------------------------- /BGP/bgpipe/topology.yml: -------------------------------------------------------------------------------- 1 | # Configure a BGP session with a Linux host on which we'll run bgpipe 2 | 3 | # Make BGP a valid node attribute so we can configure bgp.as on the node not running BGP 4 | defaults.attributes.node.bgp: 5 | 6 | provider: clab 7 | defaults.devices.linux.group_vars.docker_shell: bash 8 | defaults.device: frr 9 | 10 | nodes: 11 | rtr: 12 | module: [ bgp ] 13 | bgp.as: 65000 14 | probe: 15 | device: linux 16 | provider: clab 17 | image: netlab/bgpipe:latest 18 | bgp.as: 65100 19 | 20 | links: 21 | - rtr: 22 | probe: 23 | 24 | message: | 25 | The bgpipe lab is waiting for you ;) Connect to 'probe' with 'netlab connect 26 | 'probe'. The remote router is reachable at 172.16.0.1 and your ASN should be 27 | 65100, for example: 28 | 29 | bgpipe -o speaker --asn 65100 172.16.0.1 30 | 31 | Good luck! 32 | -------------------------------------------------------------------------------- /BGP/interface-IBGP/README.md: -------------------------------------------------------------------------------- 1 | # IBGP-Only Network Design 2 | 3 | This directory contains the lab topology described in the [IBGP Is the Better EBGP](https://blog.ipspace.net/2025/01/ibgp-better-ebgp/) blog post. 4 | 5 | The topology relies on a plugin that restructures the IBGP sessions and a custom configuration for FRRouting and Arista EOS that enforces the change of BGP next hops of reflected routes. 6 | 7 | Please submit a Pull Request if you have successfully implemented the same functionality on other platforms. 8 | -------------------------------------------------------------------------------- /BGP/interface-IBGP/ibgp.nhs/eos.j2: -------------------------------------------------------------------------------- 1 | {% for ngb in bgp.neighbors|default([]) if ngb.type == 'ibgp_localas' %} 2 | {% if loop.first %} 3 | router bgp {{ bgp.as }} 4 | {% endif %} 5 | {% for af in ['ipv4','ipv6'] if af in ngb.activate %} 6 | neighbor {{ ngb[af] }} next-hop-self 7 | {% endfor %} 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /BGP/interface-IBGP/ibgp.nhs/frr.j2: -------------------------------------------------------------------------------- 1 | router bgp {{ bgp.as }} 2 | {% for af in ['ipv4','ipv6'] %} 3 | {% for ngb in bgp.neighbors|default([]) if ngb.type == 'localas_ibgp' and af in ngb.activate %} 4 | {% if loop.first %} 5 | address-family {{ af }} 6 | {% endif %} 7 | neighbor {{ ngb[af] }} next-hop-self force 8 | {% endfor %} 9 | {% endfor %} 10 | -------------------------------------------------------------------------------- /BGP/interface-IBGP/topology.yml: -------------------------------------------------------------------------------- 1 | defaults.device: eos 2 | provider: clab 3 | 4 | plugin: [ fabric, ibgp.interface ] 5 | fabric.spines: 2 6 | fabric.leafs: 2 7 | 8 | fabric.leaf.name: leaf-{count} 9 | fabric.spine.name: spine-{count} 10 | 11 | module: [ bgp ] 12 | bgp.as: 65000 13 | 14 | groups: 15 | spines: 16 | bgp.rr: True 17 | config: [ ibgp.nhs ] 18 | leafs: 19 | config: [ ibgp.nhs ] 20 | 21 | nodes: 22 | host-1: 23 | device: linux 24 | host-2: 25 | device: linux 26 | 27 | links: 28 | - host-1: 29 | leaf-1: 30 | - host-2: 31 | leaf-2: 32 | -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/README.md: -------------------------------------------------------------------------------- 1 | # Inter-VRF DHCP relaying with redundant DHCP servers 2 | 3 | This directory contains *netlab* topology file used to test DHCP relaying from an EVPN VRF toward 4 | a set of redundant DHCP servers in the network core. 5 | 6 | ![DHCP relaying topology](evpn-dhcp-redundant-server.png) 7 | 8 | After starting the lab, the clients (*cl_a* and *cl_b*) should get DHCP-assigned IP address on their lab-facing interfaces. 9 | -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/clear-log.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # 3 | # Clear log buffers on clients and servers 4 | # 5 | echo "y"|netlab connect srv1 clear logging 6 | echo "y"|netlab connect srv2 clear logging 7 | echo "y"|netlab connect cl_a clear logging 8 | echo "y"|netlab connect cl_b clear logging 9 | -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/dhcp-client/ios.j2: -------------------------------------------------------------------------------- 1 | logging buffered 2 | service timestamp debug uptime 3 | ! 4 | do debug dhcp detail 5 | ! 6 | {% for intf in interfaces if intf.dhcp.client is defined and intf.dhcp.client %} 7 | interface {{ intf.ifname }} 8 | no ip address 9 | ip address dhcp 10 | {% endfor %} -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/dhcp-relay/eos.j2: -------------------------------------------------------------------------------- 1 | {% for intf in interfaces if intf.dhcp.server is defined and intf.vrf is defined %} 2 | {% if loop.first %} 3 | ip dhcp relay information option 4 | {% endif %} 5 | {% endfor %} 6 | ! 7 | {% for intf in interfaces if intf.dhcp.server is defined %} 8 | interface {{ intf.ifname }} 9 | {% for srv in intf.dhcp.server %} 10 | {% set helper = hostvars[srv].loopback.ipv4|ipaddr('address') %} 11 | ip helper-address {{ helper }}{% if intf.vrf is defined %} vrf default source-interface Loopback0{% endif +%} 12 | {% endfor %} 13 | {% endfor %} 14 | -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/evpn-dhcp-redundant-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/DHCP/evpn-redundant-server/evpn-dhcp-redundant-server.png -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/fetch-log.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # 3 | # Clear log buffers on clients and servers 4 | # 5 | netlab connect srv1 show log >$1-srv1.log 6 | netlab connect srv2 show log >$1-srv2.log 7 | netlab connect cl_a show log >$1-cl_a.log 8 | -------------------------------------------------------------------------------- /DHCP/evpn-redundant-server/outputs/format.yml: -------------------------------------------------------------------------------- 1 | --- 2 | addr: | 3 | {{ " {0:20} {1:>18} {2}".format("Interface","IPv4 address","Description") }} 4 | {{ "=" * 80 }} 5 | {% for n,d in nodes.items() %} 6 | {% if not loop.first %} 7 | 8 | {% endif %} 9 | {{ n }} ({{ d.loopback.ipv4 }}) 10 | {% for intf in d.interfaces if 'ipv4' in intf %} 11 | {{ "{0:20} {1:>18} {2}".format(intf.ifname,intf.ipv4,intf.name) }}{% if 'vrf' in intf 12 | %} (VRF: {{ intf.vrf }}){% endif +%} 13 | {% endfor %} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/README.md: -------------------------------------------------------------------------------- 1 | # DHCP relaying from an EVPN VRF - version 2 2 | 3 | Inspired by ../evpn-relay with the following modifications: 4 | 1. Use dnsmasq (Linux container) as DHCP server 5 | 2. Use Alpine Linux clients 6 | 3. Use SR Linux for the relay switches 7 | 4. Symmetric IRB with separate VLANs within the same client VRF towards the DHCP server (using static routes) 8 | 9 | ![DHCP relaying topology](evpn-dhcp-relay-v2.png) 10 | 11 | After starting the lab, the clients (*cl_a* and *cl_b*) should get DHCP-assigned IP address on their lab-facing interfaces. 12 | 13 | ## Note on address pool selection 14 | In this example dnsmasq selects an available pool based on the giaddress received from the relay agent. 15 | Other arrangements are possible (e.g. using the Circuit ID information added by the relay agent) 16 | -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/dhcp-client/alpine.interfaces: -------------------------------------------------------------------------------- 1 | auto lo 2 | iface lo inet loopback 3 | 4 | auto eth1 5 | iface eth1 inet dhcp 6 | -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/dhcp-client/linux.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | set -e 4 | 5 | # Remove any static IPs 6 | {% for intf in interfaces if intf.dhcp.client is defined and intf.dhcp.client %} 7 | ip addr flush dev {{ intf.ifname }} 8 | {% endfor %} 9 | 10 | exit 0 11 | -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/dhcp-server/linux.j2: -------------------------------------------------------------------------------- 1 | # Create static routes for giaddress of each leaf 2 | # Note that giaddress is used for subnet pool selection, so it cannot be a router loopback IP 3 | 4 | {% for h,v in hostvars.items() %} 5 | {% for i in v.interfaces if 'dhcp' in i and i.dhcp.relay is defined and i.ipv4 is defined %} 6 | {% for j in interfaces %} 7 | {% for n in j.neighbors if n.node==h %} 8 | ip route add {{ i.ipv4|ipaddr('address') }}/32 via {{ n.ipv4|ipaddr('address') }} 9 | {% endfor %} 10 | {% endfor %} 11 | {% endfor %} 12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/evpn-dhcp-relay-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/DHCP/evpn-relay-v2/evpn-dhcp-relay-v2.png -------------------------------------------------------------------------------- /DHCP/evpn-relay-v2/outputs/format.yml: -------------------------------------------------------------------------------- 1 | --- 2 | addr: | 3 | {{ " {0:20} {1:>18} {2}".format("Interface","IPv4 address","Description") }} 4 | {{ "=" * 80 }} 5 | {% for n,d in nodes.items() %} 6 | {% if not loop.first %} 7 | 8 | {% endif %} 9 | {{ n }} ({{ d.loopback.ipv4 }}) 10 | {% for intf in d.interfaces if 'ipv4' in intf %} 11 | {{ "{0:20} {1:>18} {2}".format(intf.ifname,intf.ipv4,intf.name) }}{% if 'vrf' in intf 12 | %} (VRF: {{ intf.vrf }}){% endif +%} 13 | {% endfor %} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /DHCP/evpn-relay/README.md: -------------------------------------------------------------------------------- 1 | # DHCP relaying from an EVPN VRF 2 | 3 | This directory contains *netlab* topology file used to test DHCP relaying from an EVPN VRF toward a DHCP server in the network core. 4 | 5 | ![DHCP relaying topology](evpn-dhcp-relay.png) 6 | 7 | After starting the lab, the clients (*cl_a* and *cl_b*) should get DHCP-assigned IP address on their lab-facing interfaces. 8 | -------------------------------------------------------------------------------- /DHCP/evpn-relay/dhcp-client/ios.j2: -------------------------------------------------------------------------------- 1 | {% for intf in interfaces if intf.dhcp.client is defined and intf.dhcp.client %} 2 | interface {{ intf.ifname }} 3 | no ip address 4 | ip address dhcp 5 | {% endfor %} -------------------------------------------------------------------------------- /DHCP/evpn-relay/dhcp-relay/eos.j2: -------------------------------------------------------------------------------- 1 | {% for intf in interfaces if intf.dhcp.server is defined and intf.vrf is defined %} 2 | {% if loop.first %} 3 | ip dhcp relay information option 4 | {% endif %} 5 | {% endfor %} 6 | ! 7 | {% for intf in interfaces if intf.dhcp.server is defined %} 8 | {% set helper = hostvars[intf.dhcp.server].loopback.ipv4|ipaddr('address') %} 9 | interface {{ intf.ifname }} 10 | ip helper-address {{ helper }}{% if intf.vrf is defined %} vrf default{% endif %} 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /DHCP/evpn-relay/evpn-dhcp-relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipspace/netlab-examples/0cc1b2e33886dd1569a12c9eaf7a6f7c8756bd0a/DHCP/evpn-relay/evpn-dhcp-relay.png -------------------------------------------------------------------------------- /DHCP/evpn-relay/outputs/format.yml: -------------------------------------------------------------------------------- 1 | --- 2 | addr: | 3 | {{ " {0:20} {1:>18} {2}".format("Interface","IPv4 address","Description") }} 4 | {{ "=" * 80 }} 5 | {% for n,d in nodes.items() %} 6 | {% if not loop.first %} 7 | 8 | {% endif %} 9 | {{ n }} ({{ d.loopback.ipv4 }}) 10 | {% for intf in d.interfaces if 'ipv4' in intf %} 11 | {{ "{0:20} {1:>18} {2}".format(intf.ifname,intf.ipv4,intf.name) }}{% if 'vrf' in intf 12 | %} (VRF: {{ intf.vrf }}){% endif +%} 13 | {% endfor %} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /DHCP/relay/dhcp-client/ios.j2: -------------------------------------------------------------------------------- 1 | {% for intf in interfaces if intf.dhcp.client is defined and intf.dhcp.client %} 2 | interface {{ intf.ifname }} 3 | no ip address 4 | ip address dhcp 5 | {% endfor %} -------------------------------------------------------------------------------- /DHCP/relay/dhcp-client/linux-clab.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | set -e 4 | # 5 | # Create ifupdown configuration file for DHCP-enabled interfaces 6 | # 7 | cat <