May
05

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 "\n"] {

# 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 "\n"] {
+>(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!

Brian McGahan, CCIE #8593, CCDE #2013::13
About Brian McGahan, CCIE #8593, CCDE #2013::13

Subscribe to INE Blog Updates