A Catch from the Codeshare: Network Translations
On the side of the road in northern Missouri just north of Mark Twain’s stomping grounds, there is a slice of hillside removed just to the side of the highway. In Arkansas, there’s a nondescript field tucked away in a state park. Short of word of mouth and this thing they call the internet, you wouldn’t be any the wiser that buried or surfaced in these two locations are an amazing variety of geodes and diamonds, respectively. In this article series I will explore recent and well-aged gems from the codeshare, highlighting inventive solutions contributed by you, the community. Please join me on this great adventure as we oscillate on the Mohs’ scale of codeshare geekery.
Network Translations
Today’s catch from the codeshare is courtesy of F5er Gregory Thiell, who shared a handy dandy iRule that masks the client bits and translates only the network. This is beneficial if you are wanting to match clientside and server side transactions at the IP layer in analysis or logging tools. It requires a data group with entries of the pre/post translation networks like so:
1.1.1.0/24 := 10.10.10.0 1.1.2.0/24 := 10.10.20.0
This will result in a client IP of 1.1.1.5 and 1.1.2.5 being translated to 10.10.10.5 and 10.10.20.5, respectively. The iRule itself utilizes a few Tcl procs to do the heavy conversion lifting between formats. Note that the first two are just mirror functions for conversion between hex and dotted decimal to represent the IP address. The third proc deals with creating a hex net mask from the CIDR length provided with the first term in each entry in the data group.
proc IPtoHex { IP } { binary scan [binary format c4 [split $IP .]] H8 Hex return $Hex } proc hexToIP { Hex } { binary scan [binary format H8 $Hex] c4 IPtmp foreach num $IPtmp { # binary scan "c" format gives signed int - the following # [expr]-ology converts to unsigned (from [binary] manpage) lappend IP [expr ($num + 0x100) % 0x100] } set IP [join $IP .] return $IP } proc CIDRtoHexNetmask { CIDR } { set zeros [expr 32 - $CIDR] set ones $CIDR set binaryCIDR [string repeat 1 $ones] append binaryCIDR [string repeat 0 $zeros] binary scan [binary format B32 $binaryCIDR] H8 HexNetmask return $HexNetmask }
And finally, the iRule itself! The logic is pretty straight forward. The first step is to read the data group against the real (or manually set) original IP address (lines 13-16,) and if there is a match (line 18,) start the conversions (lines 21-23.) Once the network mask is calculated (lines 26-28,) and after all the IP address bitwise math is done, the network can be extracted and replaced (lines 31-38,) and the final translated IP address can be utilized with the snat command (line 41.) You can uncomment the log statements at the end of the iRule (lines 45-51) for debugging purposes.
when RULE_INIT { set static::dg_net_tsl "dg_network_translations" } when CLIENT_ACCEPTED { set original_ip [IP::client_addr] # Debug -- To overwrite original_ip # set original_ip 1.1.1.5 # Read Data Group set dg_elements [class match -element $original_ip equals $static::dg_net_tsl] set original_network [getfield [lindex $dg_elements 0] "/" 1] set cidr [getfield [lindex $dg_elements 0] "/" 2] set translated_network [lindex $dg_elements 1] if { $dg_elements != "" } { # Hex conversion set hex_original_ip [call IPtoHex $original_ip] set hex_netmask [call CIDRtoHexNetmask $cidr] set hex_translated_network [call IPtoHex $translated_network] # Calculate the hostmask (= inverted netmask) set hex_hostmask [expr 0x$hex_netmask ^ 0xffffffff] binary scan [binary format I $hex_hostmask] H8 hex_hostmask set hostmask [call hexToIP $hex_hostmask] # Extract the host bits (bit-wise AND between original_ip and hostmask) set hex_host [expr 0x$hex_original_ip & 0x$hex_hostmask] binary scan [binary format I $hex_host] H8 hex_host set host [call hexToIP $hex_host] # Translate the network bits (bit-wise OR between host bits and translated_network) set hex_translated_ip [expr 0x$hex_host | 0x$hex_translated_network] binary scan [binary format I $hex_translated_ip] H8 hex_translated_ip set translated_ip [call hexToIP $hex_translated_ip] # SNAT snat $translated_ip # Debug -- Log # log local0. "Client IP (dec, hex): ($original_ip, $hex_original_ip)" # log local0. "Original network (dec): ($original_network)" # log local0. "Netmask (CIDR, hex): ($cidr, $hex_netmask)" # log local0. "Translated network (dec, hex): ($translated_network, $hex_translated_network)" # log local0. "Hostmask (dec, hex): ($hostmask, $hex_hostmask)" # log local0. "Host (dec, hex): ($host, $hex_host)" # log local0. "Translated IP (dec, hex): ($translated_ip, $hex_translated_ip)" } }
This is not only functionally very useful, it's a great learning iRule as well for examining Tcl procecures, expressions, binary scans/formats, etc. So whether you need it in your environment or not, grab this one for your lab box and start inspecting.
Hi Jason,
I was little bit surprised of the complexity of the provided iRule and ended up with a less complicated 20LinesOrLess edition...
when RULE_INIT { set static::dg_net_tsl "dg_network_translations" } when CLIENT_ACCEPTED { Read Data Group set original_ip [getfield [IP::client_addr] "%" 1] set dg_elements [class match -element $original_ip equals $static::dg_net_tsl] if { $dg_elements ne "" } then { set new_ip_prefix [findstr $dg_elements " " 1] set cidr_mask [findstr $dg_elements "/" 1 " "] IP to binary conversion binary scan [binary format c4 [split $original_ip "."]] B* original_ip_bin binary scan [binary format c4 [split $new_ip_prefix "."]] B$cidr_mask new_ip_prefix_bin IP prefix translation and binary to IP conversion binary scan [binary format B* "$new_ip_prefix_bin[string range $original_ip_bin $cidr_mask end]"] cccc oct1 oct2 oct3 oct4 set new_ip "[expr { $oct1 & 0xff } ].[expr { $oct2 & 0xff } ].[expr { $oct3 & 0xff } ].[expr { $oct4 & 0xff } ]" SNAT snat $new_ip } }
The iRule now translates the client IP by utilizing a rather simple
syntax on the IPs binary string representations. The binary syntax requires not only just fewer lines, but also performs much faster compared to utilizing complex math on a HEX level.[string range]
Cheers, Kai
- JRahmAdmin
Hi Kai, that's a great optimization! Thanks for sharing.