How to Script Large Ixia Breaking Point Network Profiles

Background

My team at SealingTech was tasked with testing the performance of a router that would be a tunnel endpoint for many different Site to Site VPN connections from various places. We were given requirements that it needed to support thousands of tunnels and VRFs and lots of bandwidth (upwards of 20 Gb/s). We had an Ixia Traffic Generator (utilizing their Breaking Point Software) to generate the traffic that would be necessary for this. The traffic generation software supports being one end of a tunnel endpoint, but it requires all of the network configuration to be setup via a web GUI.  Naturally, setting up thousands of objects in a web GUI was not something we were interested in doing manually!  We reached out to the vendor to ask if there was any way to support automating the creation of a large test like this one and their response basically amounted to “Good Luck!”

Luckily for us, we had good luck!

The Breaking Point software allows you to export configurations that you have built.  These files, although labeled as “.bpt” files are actually XML files upon closer examination. This means that if the XML schema can be reverse engineered, ALMOST ANY PIECE OF THIS DEVICE’S CONFIGURATION CAN BE AUTOMATED! Woohoo!

The next challenge was to figure out how to automate it. Surely many different programming languages can be used, but we decided on Windows Powershell for the following reasons:

  1. This testing was run in an air-gapped network. This means that it would be kind of a hassle to download the various pieces of software and modules required for some programming languages.
  2. Windows Powershell is installed on nearly every Windows system, and doesn’t really require any internet connectivity for module updates etc.
    • It handles XML natively with some built-in libraries
    • It supports working directly in the command line interactively which makes testing pieces of the script (such as editing specific XML values) very simple

Overview

The functionality of the program was very simple. We needed to be able to take a spreadsheet with all of the different IPs used for the test, and use that to create the Breaking Point “Network Neighborhood”. In order to do this, we needed to do the following:

  1. Make a simplified version of the test in the Breaking Point GUI that features 1 of every “element” you might need.
  2. Export the XML from that test and analyze each piece individually. Look at what the values are, what needs to change, and what can stay the same.
  3. Create a spreadsheet with all of the IP addresses, VLAN interfaces, Router Objects etc that are needed. This is much easier than doing it in the Breaking Point GUI because a CSV file can be created by a script, and Excel can also be used to increment and copy and paste large columns of data very easily.
  4. Once the CSV was created, we built the script to read from it and create all of the XML pieces that we determined in Step 2.

When the script runs, it looks something like this:

And when you’ve imported the new file into Breaking Point, the test looks something like this:

Technical Explanations:

This section will attempt to describe each function in detail and how it works. Obviously our scenario was very specific, so if you ever want to do something like this you may want to read how each function works. For many, it could help save time if you have to automate something like this on your own.

Function Get-FileName

This is basically a bunch of technical mumbo-jumbo that opens a prompt to load in a file (CSV or XML). Nothing crazy going on here, but it does set the intial directory to your desktop. Everything else is code that I found somewhere (probably on a Microsoft web page)


Function Get-FileName($type)
{
	$username = whoami
	$username = $username.split("\")
	$username = $username[1]
	$initialdirectory = "C:\users\$username\Desktop"
	[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")|out-null
	$OpenFileDialog = new-object system.windows.forms.openfiledialog
	$OpenFileDialog.initialdirectory = $initialDirectory
	if($type -eq "csv"){$OpenFileDialog.filter = "CSV (*.csv)|*.csv"}
	if($type -eq "xml"){$OpenFileDialog.filter = "XML (*.xml)|*.xml"}
	$OpenFileDialog.showHelp = $true
	$OpenFileDialog.showDialog()|out-null
	$OpenFileDialog.filename
}       
       

Functions ImportXML & ImportCSV

These two functions may as well be grouped together. They are complimentary functions to the “Get-Filename” shown above. They’re for importing the CSV and XML files that the script needs.

        
#Can be used to import a CSV of your choosing
1
Function ImportCSV()
{
	$filepath = Get-FileName csv
	$UserCSV = import-csv $filepath
	return $UserCSV
}

#Can be used to import an XML template of your choosing from the Breaking Point
Function ImportXML()
{
	$filepath = Get-FileName xml
	$UserXML = [xml](Get-content $filepath)
	return $UserXML
}
       

Function GetInitialDirectory

This one creates a variable that be used to map different parts of this script to your desktop.


Function GetInitialDirectory()
{
	$username = whoami
	$username = $username.split("\")
	$username = $username[1]
	$initialdirectory = "C:\users\$username\Desktop"
	return $initialdirectory
}
      

Function CreateIPSecRouters

Okay, this function is huge and messy, probably bigger and messier than it has any business being ( I think we can all relate to that!) It may look complicated, but upon close analysis it is actually simpler than it seems. As stated earlier in this post, the configuration file we need to create is all in XML and Powershell can handle XML natively with ease! This function loops through all the lines in a CSV and adds the appropriate pieces of XML. There are some pieces of this that can be particularly confusing, I’ll do my best to explain them here:
If you examine the XML file provided, you’ll find that its got many levels of nesting. This can be done with various different programs, I used Notepad++ for initial analysis and imported the file into Powershell to confirm my code. This line essentially creates a new object variable that is exactly the same as the following location in the XML: ModelExport–>Network–>NetworkModel–>Element[5]
Elements 0-3 are Interfaces, Element 4 is a VLAN, and Element 5 is an IPSec Router! *whew!*


 $newobject = $xml.modelExport.network.networkmodel.element[5].clone()
       

 

Looking at the XML further, you’ll see that this specific element is made up of 7 additional properties:

• String ID
• Reference ID (Container)
• IP Address (IP Address)
• IP Address (Gateway IP Address)
• Netmask
• IP Address (Peer_IP_Address)
• Reference ID (IPSec Configuration)

Examining this even MORE closely (lots of close examination here!) you may notice that some of these elements have the same tags. Everything is hierarchical! Powershell will actually see this as 4 different properties (some of which have multiple sub-properties). This will play into the next piece of our function (line 57-66):


		$Name = $line.name
			$VLAN = $line.container
			$IP = $line.ipaddr
			$Gateway = $line.gateway
			$netmask = $line.count
			
			$newobject.string."#text" = [string]$Name
			$newobject.reference[0]."#text" = [string]"$VLAN"
			$newobject.ip_address[0]."#text" = [string]$gateway
			$newobject.ip_address[1]."#text" = [string]$IP
			$newobject.ip_address[2]."#text" = [string]$IP
    

Notice how the $newobject.string that is being changed looks different than the other changes? That is because it’s the only element listed under “StringID”. The others all need to have an index number specified. As mentioned above, Reference 0 is the “VLAN Container”, and the IP address indexes are the three different IP addresses specified above.

The final piece of this code snippet is to add the new values to the original XML object. This command adds the new values to the XML object.


$xml.modelExport.network.networkmodel.appendchild($newobject)
       

The next section of code creates a different section of XML that is referred to as a “Domain Element” in the schema. There is some neat stuff here, so lets take a look!

This snippet of code is used to create a MAC address that needs to be inserted into the XML. The first line converts an integer into hex and the second line inserts the hex into a properly formatted MAC address.


		$MACKEY = "{0:x2}" -f $x
		$CreateMAC = "02:1a:c5:"+$MACKEY+":00:00" 
     

All of this is basically just formatting text and properly naming things for this section of XML.


		$VLAN = $i.VLAN
		$NetworkAddr = IPSplit $i.ipaddr
		$HostIP = $i.ipaddr
		$splitIP = $i.ipaddr.split(".")
		$splitPort=$splitIP[1]
		$splitHost=$splitIP[2]
		$RouterIP="10.$splitport.$splithost.2"
		$GatewayIP="10.$splitport.$splithost.1"
		if($splitport -eq "1"){$LCNum = "1";$PortNum="1"}
		if($splitport -eq "2"){$LCNum = "1";$PortNum="2"}
		if($splitport -eq "3"){$LCNum = "2";$PortNum="1"}
		if($splitport -eq "4"){$LCNum = "2";$PortNum="2"}
		$RouterName="LC$LCNum-Port$PortNum-CT$SplitHost-RTR"
		$VLANName="VLAN$Splitport$SplitHost"
		$VLANNumber="$Splitport$SplitHost"
		$Interface="interface $splitport"
    

And the rest of the function is following the same pattern we’ve seen before; Clone the template XML, change the values of all relevant parameters, and append it to the original XML object


		If ($i.Type -like "StaticHost" -and $i.container -notlike "VLAN*") 
		{
			$newobject = $xml.modelExport.network.domain[1].clone()
			$newobject.name = [string]$i.Name
			$newobject.endpoints = [string]$i.Name
			$newobject.subnet.router_ip= [string]$RouterIP
			$newobject.subnet.netmask = [string]$i.Count
			$newobject.subnet.netaddr = [string]$networkaddr
			$newobject.subnet.gateway = [string]$GatewayIP
			$newobject.subnet.innervlan = [string]$VLANNumber
			$newobject.subnet.mac_count = "1"
			$newobject.subnet.l2 = $CreateMac
			$newobject.subnet.composition.stack[0].id = [string]$i.Name
			$newobject.subnet.composition.stack[1].id = [string]$i.container
			$newobject.subnet.composition.stack[2].id = [string]$VLANName
			$newobject.subnet.composition.stack[3].id = [string]$Interface
			$newobject.subnet.range.min = [string]$i.ipaddr
			$newobject.subnet.range.max = [string]$i.ipaddr
			
			#Append New Object
			$xml.modelExport.network.appendchild($newobject)
		}
  

Function CreateStaticHostsVlans

This function works just like the others that we’ve seen so far. It clones an existing element, changes the values in it, and appends it to the original XML object. I’ll refrain from doing this one line by line since there isn’t anything new in the code here, just different pieces of XML to adjust


#Creates the static hosts entries in the XML File with VLAN Containers
Function CreateStaticHostsVlans($CSV, $XML)
{
	####Loop to create IPv4 Elements
	Foreach ($i in $csv)
	{
		If ($i.Type -like "StaticHost") 
		{
		
			$newobject = $xml.modelExport.network.networkModel.element[7].clone()														#######Need a better way to select the element
			
			$name = $i.name
			$container = $i.container
			$VLAN = $i."VLAN"
			
			$newobject.tags.tag.id = $i.Name
			$newobject.string."#text" = $name
			$newobject.ip_address."#text" = $i.ipaddr
			$newobject.reference."#text" = [string]$container
			
			
			#Append New Object
			$xml.modelExport.network.networkModel.appendchild($newobject)
		
		}
	}
return $xml
	
}
     

Function CreateVLANs

This function also works like the others so far. It clones an existing object from the XML and edits the values. However there is some cool stuff in it that we will go over. Specifically, the creation of MAC addresses for the VLAN interfaces. These lines are highlighted in yellow, everything else is something that has been seen previously. Again, this code looks confusing, but is actually fairly simple. The variables “counter” and “iteration” are converted into hex and placed into the proper format for a MAC address. Counter is used for the last two digits of the MAC, and Iteration is used for the two digits before that.


#Creates the Vlan entries in the XML File
Function CreateVLANs($CSV, $XML)
{	
	$iteration = 1
	$counter = 1
	$x=6
	$UniqueVLANs = $CSV|where{$_.type -like "VLANInt"}
	#Runs through all Unique VLANs and adds them to the XML
	foreach($Line in $UniqueVLANs)
	{
		#Create Variables from Each Line in the Object and format them properly for the XML
		$VLAN = $Line.Name
		$VLANTag = $VLAN.substring(4)
		$Interface = $Line.Container
		$Interface = $Interface.ToLower()
		
		#Convert the VLAN Value to hex and create a properly formed MAC with the key
		$MACKEY = "{0:x2}" -f $x
		$MACKEY2 = "{0:x2}" -f $counter
		$MACKEY3 = "{0:x2}" -f $iteration
		$CreateMAC = "02:1a:c5:00:"+$MACKEY3+":"+$MacKey2
		
		#Set XML Values for VLANs
		$newobject = $xml.modelExport.network.networkmodel.element[4].clone()
		$newobject.mac_address."#text" = $CreateMac
		$newobject.int[1]."#text" = "$VLANTag"
		$newobject.string."#text" = "$VLAN"	
		$newobject.reference."#text" = $Interface	#Sets the "container" for the VLAN
		
		$xml.modelExport.network.networkmodel.appendchild($newobject)
		$x = $x+1
		if ($counter -ne 200) {$counter = $counter + 1}
		if ($counter -eq 200) {$counter = 1;$iteration = $iteration +1}
	}
	
	#TODO - Currently no good way to enter in Interface Container since it isn't being fed to the ForEach loop. 
	
	return $xml
}
      

Function IPSplit

This function takes a /24 IP Address and converts it into a Network Address (i.e. 192.168.1.5 would be turned into 192.168.1.0). This was needed because our CSV file didn’t list network addresses, it listed gateways and peers.


Function IPSPLIT($IPString) #This takes an IP address and turns it into a Network Address
{
		$splits = $ipstring.split(".")
		$Octet1 = $splits[0]
		$Octet2 = $splits[1]
		$Octet3 = $splits[2]
		$Octet4 = $splits[3]
		$NetworkAddress = $splits[0]+"."+$splits[1]+"."+$splits[2]+".0"
		return $NetworkAddress
}
     

Function “Main”

This isn’t really a function in Powershell, but I’ve seen the terminology used in other programs to describe the parts of the program that actually run. It winds up being pretty simple since we did all of the hard stuff in the functions.

PrintColorLogo and PrintLine are functions that make the Powershell window pretty and display our company logo. They are part of a module I created called “STColorFunctions.psm1” available with this script.

The script then creates popup windows instructing you to give it input files. One for a CSV, one for an XML base file. Once you’ve selected those, it passes those variables into the functions from earlier. After that, it saves the file to your desktop with a “.bpt” extension. This is the extension that Breaking Point requires for uploading the file.


#Makes the window pretty
PrintColorLogo
PrintLine

#Creates an object and a popup window.
$wshell = new-object -ComObject wscript.Shell
$wshell.popup("Please select the CSV to be used",0,"Select a CSV",0x0)

$CSV = ImportCSV
$wshell.popup("Please select the XML to be used",0,"Select a CSV",0x0)
$XML = ImportXML

#Makes changes to the XML Files.
CreateVLANs $CSV $XML
CreateIpsecRouters $CSV $XML
CreateStaticHostsVlans $CSV $XML

#Saves the XML File.
$Desktop = GetInitialDirectory
$FullString = $Desktop + "\test.bpt"
$xml.Save($FullString)
echo 'Your XML file has been saved to your desktop as "test.bpt"'
    

Tips and Tricks

Using Powershell to “Reformat” an exported Breaking Point file

When exporting a Breaking Point configuration file from the Web GUI, the file is exported as a .bpt file. This is just an XML file with a different extension. However, if you try to view it in notepad or notepad++ the contents of the file are all on one line making it very difficult to read. I’ve learned that importing the file into powershell as XML and then re-saving it reformats the file to be properly indented and easily readable.


$file = [xml](get-content FILENAME)
$File.save(“C:\users\USERNAME\desktop\postsavexml.xml”)
    

Using Powershell to “Traverse” an exported Breaking Point File

If you ever have to write one of these of your own, this is a great way to see how the different pieces of the XML schema work and you can also experiment with editing them to see the results. Following this code block, you can see how you can traverse the XML one level at a time and see how each parameter is stored and edited.


$file = [xml](get-content FILENAME)
$File
$file.modelexport
$file.modelexport.network
$file.modelexport.network.networkmodel
$file.modelexport.network.networkmodel.element
$file.modelexport.network.networkmodel.element[0]
    

Q&A

How did you make that giant Excel File?

Great question! We actually did a bit of scripting to make the Excel file as well. It essentially boiled down to using nested loops to print all of the necessary lines to a CSV file. For things that were easy enough to do in excel, we just dragged and had excel either copy the column, or increment the column. The scripts used to do this are attached to this blog post.

What if I need to do something different?

There is a very good chance you WILL need to do something different. My recommendation for the process is as follows:

1. Use this guide as a template for how you can scale your test
2. Make a simplified version of your test in the Breaking Point GUI that features 1 of every “element” you might need.
3. Export the XML from that test and analyze each piece individually. Look at what the values are, what needs to change, and what can stay the same.
4. Now that you know the values of each element, you should be able to clone them, change the relevant values and see if the new XML you’ve created can be imported into BreakingPoint successfully.

Does this really save any time?

Depending on how big your test is, it really could save time. If you have to type in 80 lines into the Breaking Point GUI, maybe it won’t save time. If you would have to type 8000 lines into the GUI, there is a good chance it would save you a lot of time. Additionally, you’re much less likely to have a hard to find mistake if a script is making all of those lines. As with any approach, your mileage may vary.

Interested in using this? Download this zip file– it has all of the different documents needed to edit this script. Happy Automating! 

 

 

Leave a comment

Your email address will not be published. Required fields are marked *