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!

May
02

A voice lab rack usually utilizes dedicated piece of hardware to simulate PSTN switch. Commonly, you can find a Cisco router in this role, with a number of E1/T1 cards set to emulate ISDN network side. It perfectly suits the function, switching ISDN connections between the endpoints. Additionally, it is often required to have an “independent” PSTN phone connected to the PSTN switch, in order to represent “outside” dialing patterns - such as 911, 999, 411 1-800/900 numbers. The most obvious way to do this is to enable a CallManager Express on the PSTN router, and register either hardware IP Phone or any of IP Soft-phones (such as IP Blue or CIPC) with the CME system.

However, there is another way to accomplish the same goal using IOS functionality solely. It relies on the IP-to-IP gateway feature, called “RTP loopback” session target. It is intended to be used for VoIP call testing, but could be easily utilized to loopback incoming PSTN calls to themselves. Let’s say we want PSTN router to respond to incoming calls to an emergency number 911. Here is how a configuration would look like:

PSTN:
voice service voip
allow-connections h323 to h323
!
interface Loopback0
ip address 177.254.254.254 255.255.255.255
!
dial-peer voice 911 voip
destination-pattern 911
session target ipv4:177.254.254.254
incoming called-number 999
tech-prefix 1#
!
dial-peer voice 1911 voip
destination-pattern 1#911
session target loopback:rtp
incoming called-number 1#911

The trick is that only IP-to-IP calls could be looped back. Because of that, we need to redirect the incoming PSTN call to the router itself first, in order to establish an incoming VoIP call leg.

While this approach permits VoIP call testing, it lacks one important feature, available with the “real” PSTN phone: placing calls from the PSTN phone to the in-rack phones. However, you can always use “csim start” command on the PSTN router to overcome this obstacle. Have fun!

Jan
28

Let's say you get a bunch of inexpensive (but a bit outdated) routers (36XX or 72Xx) and some really nice (maybe not so cheap) Cisco switches (e.g. 3550/3560) and you would like to provide a VPLS-like service to your customers. Since VPLS is a service available only on more powerful Cisco platforms, we have to figure a way to simulate Multipoint Ethernet L2 VPN over a packet switching network (PSN) using only "convenient" point-to-point L2 VPN services.

Let model a situation where we have a number of routers connected over (PSN), with an ethernet switch connected to router at every location:

VPLS with L2TPV3

What we can do, is connect ethernet ports using pseudowires to form a virtual ring topology over PSN. That is, refeferring to our picture, xconnect routers' ethernet ports counter-clockwise, say xconnect E0/0 of R3 with E0/1 of R4, then E0/0 of R4 with E0/1 of R5 and finally E0/0 of R5 with E0/1 of R3. Effectively, we will form an ethernet ring, partially connected over convenient switches, and partially using L2VPN pseudowires. Router configurations look pretty much similar, for example at R3 we would have something like this

R3:
pseudowire-class PW_CLASS
encapsulation l2tpv3
ip local interface Loopback0
!
interface Loopback0
ip address 150.1.3.3 255.255.255.255

!
! Xconnecting E0/0 of R3 with E0/1 of R4
!
interface Ethernet0/0
no ip address
xconnect 150.1.4.4 34 encapsulation l2tpv3 pw-class PW_CLASS

!
! Xconnecting E0/1 of R3 with E0/0 of R5
!
interface Ethernet0/1
no ip address
xconnect 150.1.5.5 35 pw-class PW_CLASS

!
! Frame-Relay is used to connect to other routers (PSN network)
!
interface Serial1/0
no ip address
encapsulation frame-relay
!
interface Serial1/0.34 point-to-point
ip address 150.1.34.3 255.255.255.0
frame-relay interface-dlci 304
!
interface Serial1/0.35 point-to-point
ip address 150.1.35.3 255.255.255.0
frame-relay interface-dlci 305

!
! OSPF is used as a sample IGP
!
router ospf 1
router-id 150.1.3.3
log-adjacency-changes
network 0.0.0.0 255.255.255.255 area 0

Speaking honestly, it's not "classic" VPLS in true sense:

Firstly, STP should be running over ring topology, in order to block redundant ports. One can use star topology and disable STP, but this will introduce a single point of failure into the network. Classic VPLS does not run STP over packet core, only a full-mesh of pseudowires.

Secondly, there is no MAC-address learning for pseudowires, since they are point-to-point in essense. MAC addresses are learned by switches, and this impose a usual scalability restriction (though cisco switches may allow you to scale to a few thousands of MAC addresses in their tables).

However, this is funny and simple example of how you can use a simple concept to come up with a more complicated solution.

Subscribe to INE Blog Updates