This weekend while working on content updates for CCIE R&S Version 5, I ran into an interesting problem. In order to test some nuances of routing protocol updates and packet fragmentation, I was trying to generate BGP UPDATE messages that would exceed the transit MTU. To do this I manually created a bunch of Loopback interfaces and did a redistribute connected into BGP. When I looked at the packet capture details, I started to realize how many routes I'd actually need in order to fill up the packet sizes. After wasting about 30 minutes copying and pasting new Loopbacks over and over, I decided to come up with a better automated solution instead. I thought, “why not just have the router generate its own random Loopback addresses and then advertise them into BGP?” Well surprisingly I actually got it to work, despite my amateur at best coding skills.

The following TCL script is used to generate a given number of Loopback interfaces with random IPv4 and IPv6 addresses. To use it simply start the tclsh from the IOS CLI, paste the procedure in, then invoke it with generate_loopbacks X, where “X” is the number of routes you want to generate. Note that I didn’t add any error checking for overlapping addresses or invalid address and mask combinations. If someone wants to update the script to account for this, please feel free to do so and I’ll throw 100 rack rental tokens your way for the trouble. Edit: Special thanks to Jason Cook for adding the error checking for me.

A quick demo of the script in action can be found after the jump.

The script:

proc generate_loopbacks {x} { # random number generator

proc rand_range { min max } { return [expr int(rand() * ($max - $min)) + $min] } # define subnet mask lengths

set len(1) 128.0.0.0

set len(2) 192.0.0.0

set len(3) 224.0.0.0

set len(4) 240.0.0.0

set len(5) 248.0.0.0

set len(6) 252.0.0.0

set len(7) 254.0.0.0

set len(8) 255.0.0.0

set len(9) 255.128.0.0

set len(10) 255.192.0.0

set len(11) 255.224.0.0

set len(12) 255.240.0.0

set len(13) 255.248.0.0

set len(14) 255.252.0.0

set len(15) 255.254.0.0

set len(16) 255.255.0.0

set len(17) 255.255.128.0

set len(18) 255.255.192.0

set len(19) 255.255.224.0

set len(20) 255.255.240.0

set len(21) 255.255.248.0

set len(22) 255.255.252.0

set len(23) 255.255.254.0

set len(24) 255.255.255.0

set len(25) 255.255.255.128

set len(26) 255.255.255.192

set len(27) 255.255.255.224

set len(28) 255.255.255.240

set len(29) 255.255.255.248

set len(30) 255.255.255.252

set len(31) 255.255.255.254

set len(32) 255.255.255.255 # Iterate the loop $x times for {set n 1} {$n<=$x} {incr n 1} { # generate random IPv4 address

set a [rand_range 1 223]

set b [rand_range 1 255]

set c [rand_range 1 255]

set d [rand_range 1 255] # generate random IPv4 mask

set y [rand_range 1 32] # generate random IPv6 address

set e [format %x [rand_range 1 65534]]

set f [format %x [rand_range 1 65534]]

set g [format %x [rand_range 1 65534]]

set h [format %x [rand_range 1 65534]]

set i [format %x [rand_range 1 65534]]

set j [format %x [rand_range 1 65534]]

set k [format %x [rand_range 1 65534]] # generate random IPv6 mask

set z [rand_range 16 64] # set error check variable

set m 0 # set $LOOBACK_NUMBER

set LOOPBACK_NUMBER [expr 10000 + $n] # send IOS exec commands

set OUTPUT [ ios_config "interface Loopback$LOOPBACK_NUMBER" "ip address $a.$b.$c.$d $len($y)" "ipv6 address 2001:$e:$f:$g:$h:$i:$j:$k/$z" ] # Split the OUTPUT variable into individual lines, and for each line place it into the variable LINE

foreach LINE [split $OUTPUT "

"] { # check if the LINE variable contains an indication that there is a problem with a random address

# and if so, set a variable m to a specific value

if { [regexp "is overlapping with" $LINE] } {

set m 1 } elseif { [regexp "overlaps with" $LINE] } {

set m 1 } elseif { [regexp "Bad mask" $LINE] } {

set m 1 } # if the variable m is 1 decrement the variable n used to control the for loop by 1

# forcing the most recent loopback to be re-iterated by the above script

if { [expr $m==1] } {

incr n -1 }

} } }

Below is a basic demo of the script in action. R1 and R2 are directly connected on the IPv4 network 10.0.0.0/24 and the IPv6 network 2001::/64. They are peering EBGP, and R1 is doing redistribute connected into BGP in both IPv4 Unicast and IPv6 Unicast address families.

R1#show ip int brief Interface IP-Address OK? Method Status Protocol

GigabitEthernet1 unassigned YES NVRAM up up

GigabitEthernet1.10 10.0.0.1 YES manual up up

GigabitEthernet2 unassigned YES NVRAM administratively down down

GigabitEthernet3 unassigned YES NVRAM administratively down down R1#show ipv6 int brief GigabitEthernet1 [up/up]

unassigned

GigabitEthernet1.10 [up/up]

FE80::250:56FF:FE8D:4B00

2001::1

GigabitEthernet2 [administratively down/down]

unassigned

GigabitEthernet3 [administratively down/down]

unassigned R1#sh run | s bgp router bgp 1

bgp log-neighbor-changes

neighbor 10.0.0.2 remote-as 2

neighbor 2001::2 remote-as 2

!

address-family ipv4

redistribute connected

neighbor 10.0.0.2 activate

no neighbor 2001::2 activate

exit-address-family

!

address-family ipv6

redistribute connected

neighbor 2001::2 activate

exit-address-family R2#sh bgp ipv4 unicast summary BGP router identifier 10.0.0.2, local AS number 2

BGP table version is 144, main routing table version 144

1 network entries using 248 bytes of memory

1 path entries using 120 bytes of memory

1/1 BGP path/bestpath attribute entries using 240 bytes of memory

1 BGP AS-PATH entries using 24 bytes of memory

0 BGP route-map cache entries using 0 bytes of memory

0 BGP filter-list cache entries using 0 bytes of memory

BGP using 632 total bytes of memory

BGP activity 74/72 prefixes, 74/72 paths, scan interval 60 secs Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd

10.0.0.1 4 1 22 19 144 0 0 00:13:52 1 R2#sh bgp ipv6 unicast summary BGP router identifier 10.0.0.2, local AS number 2

BGP table version is 4, main routing table version 4

1 network entries using 272 bytes of memory

1 path entries using 144 bytes of memory

1/1 BGP path/bestpath attribute entries using 240 bytes of memory

1 BGP AS-PATH entries using 24 bytes of memory

0 BGP route-map cache entries using 0 bytes of memory

0 BGP filter-list cache entries using 0 bytes of memory

BGP using 680 total bytes of memory

BGP activity 74/72 prefixes, 74/72 paths, scan interval 60 secs Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd

2001::1 4 1 10 8 4 0 0 00:04:14 1 R2#show bgp ipv4 unicast BGP table version is 144, local router ID is 10.0.0.2

Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,

r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter,

x best-external, a additional-path, c RIB-compressed,

Origin codes: i - IGP, e - EGP, ? - incomplete

RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path

r> 10.0.0.0/24 10.0.0.1 0 0 1 ? R2#show bgp ipv6 unicast BGP table version is 4, local router ID is 10.0.0.2

Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,

r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter,

x best-external, a additional-path, c RIB-compressed,

Origin codes: i - IGP, e - EGP, ? - incomplete

RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path

r> 2001::/64 2001::1 0 0 1 ?

From the above output we can see that R2's only BGP routes right now are the directly connected links that R1's redistributing. Now R1 invokes the TCL script:

R1#tclsh

R1(tcl)#proc generate_loopbacks {x} {

+>(tcl)#

+>(tcl)# # random number generator

+>(tcl)# proc rand_range { min max } { return [expr int(rand() * ($max - $min)) + $min] }

+>(tcl)#

+>(tcl)# # define subnet mask lengths

+>(tcl)# set len(1) 128.0.0.0

+>(tcl)# set len(2) 192.0.0.0

+>(tcl)# set len(3) 224.0.0.0

+>(tcl)# set len(4) 240.0.0.0

+>(tcl)# set len(5) 248.0.0.0

+>(tcl)# set len(6) 252.0.0.0

+>(tcl)# set len(7) 254.0.0.0

+>(tcl)# set len(8) 255.0.0.0

+>(tcl)# set len(9) 255.128.0.0

+>(tcl)# set len(10) 255.192.0.0

+>(tcl)# set len(11) 255.224.0.0

+>(tcl)# set len(12) 255.240.0.0

+>(tcl)# set len(13) 255.248.0.0

+>(tcl)# set len(14) 255.252.0.0

+>(tcl)# set len(15) 255.254.0.0

+>(tcl)# set len(16) 255.255.0.0

+>(tcl)# set len(17) 255.255.128.0

+>(tcl)# set len(18) 255.255.192.0

+>(tcl)# set len(19) 255.255.224.0

+>(tcl)# set len(20) 255.255.240.0

+>(tcl)# set len(21) 255.255.248.0

+>(tcl)# set len(22) 255.255.252.0

+>(tcl)# set len(23) 255.255.254.0

+>(tcl)# set len(24) 255.255.255.0

+>(tcl)# set len(25) 255.255.255.128

+>(tcl)# set len(26) 255.255.255.192

+>(tcl)# set len(27) 255.255.255.224

+>(tcl)# set len(28) 255.255.255.240

+>(tcl)# set len(29) 255.255.255.248

+>(tcl)# set len(30) 255.255.255.252

+>(tcl)# set len(31) 255.255.255.254

+>(tcl)# set len(32) 255.255.255.255

+>(tcl)#

+>(tcl)## Iterate the loop $x times

+>(tcl)#

+>(tcl)# for {set n 1} {$n<=$x} {incr n 1} {

+>(tcl)#

+>(tcl)# # generate random IPv4 address

+>(tcl)# set a [rand_range 1 223]

+>(tcl)# set b [rand_range 1 255]

+>(tcl)# set c [rand_range 1 255]

+>(tcl)# set d [rand_range 1 255]

+>(tcl)#

+>(tcl)# # generate random IPv4 mask

+>(tcl)# set y [rand_range 1 32]

+>(tcl)#

+>(tcl)# # generate random IPv6 address

+>(tcl)# set e [format %x [rand_range 1 65534]]

+>(tcl)# set f [format %x [rand_range 1 65534]]

+>(tcl)# set g [format %x [rand_range 1 65534]]

+>(tcl)# set h [format %x [rand_range 1 65534]]

+>(tcl)# set i [format %x [rand_range 1 65534]]

+>(tcl)# set j [format %x [rand_range 1 65534]]

+>(tcl)# set k [format %x [rand_range 1 65534]]

+>(tcl)#

+>(tcl)# # generate random IPv6 mask

+>(tcl)# set z [rand_range 16 64]

+>(tcl)#

+>(tcl)# # set error check variable

+>(tcl)# set m 0

+>(tcl)#

+>(tcl)# # set $LOOBACK_NUMBER

+>(tcl)# set LOOPBACK_NUMBER [expr 10000 + $n]

+>(tcl)#

+>(tcl)# # send IOS exec commands

+>(tcl)# set OUTPUT [ ios_config "interface Loopback$LOOPBACK_NUMBER" "ip address $a.$b.$c.$d $len($y)" "ipv6 address 2001:$e:$f:$g:$h:$i:$j:$k/$z" ]

+>(tcl)#

+>(tcl)# # Split the OUTPUT variable into individual lines, and for each line place it into the variable LINE

+>(tcl)# foreach LINE [split $OUTPUT "

"] {

+>(tcl)#

+>(tcl)# # check if the LINE variable contains an indication that there is a problem with a random address

+>(tcl)# # and if so, set a variable m to a specific value

+>(tcl)# if { [regexp "is overlapping with" $LINE] } {

+>(tcl)# set m 1 } elseif { [regexp "overlaps with" $LINE] } {

+>(tcl)# set m 1 } elseif { [regexp "Bad mask" $LINE] } {

+>(tcl)# set m 1 }

+>(tcl)#

+>(tcl)# # if the variable m is 1 decrement the variable n used to control the for loop by 1

+>(tcl)# # forcing the most recent loopback to be re-iterated by the above script

+>(tcl)# if { [expr $m==1] } {

+>(tcl)# incr n -1 }

+>(tcl)# }

+>(tcl)#

+>(tcl)# }

+>(tcl)#

+>(tcl)#}

R1(tcl)#

R1(tcl)#generate_loopbacks 100

After a few minutes the script should be done and R1 should be advertising the new routes into BGP:

R2#show bgp ipv4 unicast

BGP table version is 210, local router ID is 10.0.0.2

Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,

r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter,

x best-external, a additional-path, c RIB-compressed,

Origin codes: i - IGP, e - EGP, ? - incomplete

RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path

*> 6.95.74.8/31 10.0.0.1 0 0 1 ?

r> 10.0.0.0/24 10.0.0.1 0 0 1 ?

*> 17.48.0.0/12 10.0.0.1 0 0 1 ?

*> 18.96.0.0/12 10.0.0.1 0 0 1 ?

*> 20.149.128.0/19 10.0.0.1 0 0 1 ?

*> 24.128.0.0/12 10.0.0.1 0 0 1 ?

*> 38.223.64.0/23 10.0.0.1 0 0 1 ?

*> 45.250.118.192/26

10.0.0.1 0 0 1 ?

*> 46.43.110.166/31 10.0.0.1 0 0 1 ?

*> 53.29.128.0/17 10.0.0.1 0 0 1 ?

*> 54.192.224.0/20 10.0.0.1 0 0 1 ?

*> 54.198.33.64/26 10.0.0.1 0 0 1 ?

*> 59.137.166.128/27

10.0.0.1 0 0 1 ?

*> 63.218.166.192/26

10.0.0.1 0 0 1 ?

*> 64.0.0.0/4 10.0.0.1 0 0 1 ?

*> 82.10.112.0/20 10.0.0.1 0 0 1 ?

*> 84.223.226.96/27 10.0.0.1 0 0 1 ?

*> 86.129.194.64/27 10.0.0.1 0 0 1 ?

*> 90.91.0.0/18 10.0.0.1 0 0 1 ?

*> 90.176.106.0/23 10.0.0.1 0 0 1 ?

[snip] R2#show bgp ipv6 unicast

BGP table version is 88, local router ID is 10.0.0.2

Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,

r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter,

x best-external, a additional-path, c RIB-compressed,

Origin codes: i - IGP, e - EGP, ? - incomplete

RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path

r> 2001::/64 2001::1 0 0 1 ?

*> 2001:434::/30 2001::1 0 0 1 ?

*> 2001:613:93FF:8B40::/60

2001::1 0 0 1 ?

*> 2001:7E2:8398::/46

2001::1 0 0 1 ?

*> 2001:86D:FA5C:4000::/51

2001::1 0 0 1 ?

*> 2001:8AD:9170::/44

2001::1 0 0 1 ?

*> 2001:136F:E000::/37

2001::1 0 0 1 ?

*> 2001:156A:E238::/49

2001::1 0 0 1 ?

*> 2001:169C::/30 2001::1 0 0 1 ?

*> 2001:192D:B2EC:2548::/61

2001::1 0 0 1 ?

*> 2001:1A00::/23 2001::1 0 0 1 ?

*> 2001:1C00::/23 2001::1 0 0 1 ?

[snip]

Feel free to use and modify the script any way you like!