rfc 4291
1 TopicTCL procedures to compress/expand a IPv6 address notation
Problem this snippet solves: Hi Folks, the iRule below contains two TCL procedures to convert an IPv6 address from/to human readable IPv6 address notations within iRules. The compress_ipv6_addr procedure shrinks an IPv6 address notation by removing leading zeros of each individual IPv6 address group (as defined in RFC 4291 Section 2.2.1) and by replacing the longest range of consecutive zero value IPv6 address groups with the :: notation (as defined in RFC 4291 Section 2.2.2). If two or more zero value IPv6 address group ranges have identical lengths, then the most significant IPv6 address groups will be replaced. If the input IPv6 address contains a mixed IPv6 and IPv4 notation (as defined in RFC 4291 Section 2.2.3), the mixed notation will be kept as is. ----------------------------------- compress_ipv6_addr ----------------------------------------- Input: 0000:00:0000:00:0000:0:00:0000 Output: :: Time: 16 clicks Input: 0:00:000:0000:000:00:0:0001 Output: ::1 Time: 15 clicks Input: 00:000:0000:affe:affe:0000:000:0%eth0 Output: ::affe:affe:0:0:0%eth0 Time: 20 clicks Input: 2001:0022:0333:4444:0001:0000:0000:0000%1 Output: 2001:22:333:4444:1::%1 Time: 20 clicks Input: 2001:1:02:003:0004::0001%2 Output: 2001:1:2:3:4::1%2 Time: 13 clicks Input: 2001:0123:0:00:000:0000:192.168.1.1%3 Output: 2001:123::192.168.1.1%3 Time: 19 clicks Input: 0001:0001::192.168.1.1%4 Output: 1:1::192.168.1.1%4 Time: 11 clicks ----------------------------------- compress_ipv6_addr ----------------------------------------- The expand_ipv6_addr procedure expands a compressed IPv6 notation by zero padding each individual IPv6 address group to its full 16 bit representation (as defined in RFC 4291 Section 2.2.1). If the input IPv6 address contains the truncated :: notation (as defined in RFC 4291 Section 2.2.2), the omitted zero value IPv6 address groups will be restored. If the IPv6 address contains a mixed IPv6 and IPv4 address notation (as defined in RFC 4291 Section 2.2.3), the IPv4 address will be converted into two consecutive IPv6 address groups. If the input contains a malformed IPv6 address which cannot be expanded to a full 128bit IPv6 address, the output will be an empty string. ------------------------------------ expand_ipv6_addr ----------------------------------------------------- Input: :: Output: 0000:0000:0000:0000:0000:0000:0000:0000 Time: 11 clicks Input: ::1 Output: 0000:0000:0000:0000:0000:0000:0000:0001 Time: 16 clicks Input: ::1:2%eth0 Output: 0000:0000:0000:0000:0000:0000:0001:0002%eth0 Time: 15 clicks Input: 2001::1%1 Output: 2001:0000:0000:0000:0000:0000:0000:0001%1 Time: 16 clicks Input: 2001:1:22:333:4444::%2 Output: 2001:0001:0022:0333:4444:0000:0000:0000%2 Time: 21 clicks Input: 2001:123::ff:192.168.1.1%3 Output: 2001:0123:0000:0000:0000:00ff:c0a8:0101%3 Time: 29 clicks Input: 2001:192.168.1.1::10.10.10.10%4 Output: 2001:c0a8:0101:0000:0000:0000:0a0a:0a0a%4 Time: 27 clicks ------------------------------------ expand_ipv6_addr ----------------------------------------------------- Note: Both procedures are able to handle % IPv6 Zone ID suffixes (as defined in RFC 6874) respectively F5's Route Domain notations. Performance considerations: Both procedures are performance optimized to maintain a reasonable performance at high execution rates. The compress_ipv6_addr procedure uses two aligned [string map] commands to remove any leading zeros without breaking the individual groups, followed by a [switch] syntax to detect the longest range of consecutive zero value groups and to execute just a simple [string range] command or a combination of the [substr] + [findstr] commands to perform the final :: truncation. The expand_ipv6_addr procedure is a little more sophisticated, since it is required to parse the input IPv6 address on a per IPv6 address group basis to zero pad the individual groups, to detect and convert embedded IPv4 addresses and to finally restore the :: truncation. To reduce the required CPU cycles the expand_ipv6_addr procedure makes use of the $static::ipv6_grp_filler() , $static::ipv6_addr_filler() and $static::ipv6_dec_map() array variables (defined during RULE_INIT ) to allow a very fast lookup of the required IPv6 address group zero paddings, the length of the zero value IPv6 address groups to insert and to translate IPv4 to IPv6 information. Cheers, Kai How to use this snippet: The iRule below contains a RULE_INIT event which outlines the procedure usage. Enjoy! Code : when RULE_INIT { # Initialize the array used to expand compressed IPv6 groups to 16 bit array set static::ipv6_grp_filler { "1" "000" "2" "00" "3" "0" "4" "" } # Initialize the array used to expand compressed IPv6 addresses to 128 bit array set static::ipv6_addr_filler { "0" "0000:0000:0000:0000:0000:0000:0000:0000" "5" "0000:0000:0000:0000:0000:0000:0000" "10" "0000:0000:0000:0000:0000:0000" "15" "0000:0000:0000:0000:0000" "20" "0000:0000:0000:0000" "25" "0000:0000:0000" "30" "0000:0000" "35" "0000" "40" "" } # Initialize the array used to perform a IPv4 (decimal 0-255) to IPv6 (hex 00-FF) conversation. for { set i 0 } { $i <= 255 } { incr i } { set static::ipv6_dec_map($i) [format %02x $i] } # # Example procedure calls (samples can be removed) # set input "2001:0001:0022:0333:4444:0:0:0:1%1" set output [call compress_ipv6_addr $input] log local0.debug "Input: $input Output: $output" set input "2001:ef:123::192.168.1.1%2" set output [call expand_ipv6_addr $input] log local0.debug "Input: $input Output: $output" } proc compress_ipv6_addr { addr } { # Enumerate and store IPv6 ZoneID / Route Domain suffix if { [set id [getfield $addr "%" 2]] ne "" } then { set id "%$id" set addr [getfield $addr "%" 1] } # X encode (e.g. :0001 becomes :X1) leading zeros on the individual IPv6 address groups (left orientated searches) set addr [string map [list ":0000" ":X" ":000" ":X" ":00" ":X" ":0" ":X" "|0000" "X" "|000" "X" "|00" "X" "|0" "X" ] "|$addr|"] # Restoring the required X encoded zeros (e.g. :X: becomes :0:) while removing any other X encodings and | separators (right orientated searches) set addr [string map [list "X:" "0:" "X|" "0" "X." "0." "X" "" "|" "" ] $addr] # Find the longest range of consecutive zero value IPv6 address groups and then replace the most significant groups with the :: notation. switch -glob -- $addr { "*::*" { #Already compressed } "0:0:0:0:0:0:0:0" { set addr "::" } "0:0:0:0:0:0:0:*" { set addr ":[string range $addr 13 end]" } "*:0:0:0:0:0:0:0" { set addr "[string range $addr 0 end-13]:" } "0:0:0:0:0:0:*" { set addr ":[string range $addr 11 end]" } "*:0:0:0:0:0:0:*" { set addr "[substr $addr 0 ":"]::[findstr $addr ":0:0:0:0:0:0:" 13]" } "*:0:0:0:0:0:0" { set addr "[string range $addr 0 end-11]:" } "0:0:0:0:0:*" { set addr ":[string range $addr 9 end]" } "*:0:0:0:0:0:*" { set addr "[substr $addr 0 ":0:"]::[findstr $addr ":0:0:0:0:0:" 11]" } "*:0:0:0:0:0" { set addr "[string range $addr 0 end-9]:" } "0:0:0:0:*" { set addr ":[string range $addr 7 end]" } "*:0:0:0:0:*" { set addr "[substr $addr 0 ":0:0:"]::[findstr $addr ":0:0:0:0:" 9]" } "*:0:0:0:0" { set addr "[string range $addr 0 end-7]:" } "0:0:0:*" { set addr ":[string range $addr 5 end]" } "*:0:0:0:*" { set addr "[substr $addr 0 ":0:0:0:"]::[findstr $addr ":0:0:0:" 7]" } "*:0:0:0" { set addr "[string range $addr 0 end-5]:" } "0:0:*" { set addr ":[string range $addr 3 end]" } "*:0:0:*" { set addr "[substr $addr 0 ":0:0:"]::[findstr $addr ":0:0:" 5]" } "*:0:0" { set addr "[string range $addr 0 end-3]:" } } # Append the previously extracted IPv6 ZoneID / Route Domain suffix and return the compressed IPv6 address return "$addr$id" } proc expand_ipv6_addr { addr } { if { [catch { # Enumerating and storing IPv6 ZoneID / Route Domain suffix if { [set id [getfield $addr "%" 2]] ne "" } then { set id "%$id" set addr [getfield $addr "%" 1] } # Parsing the first IPv6 address block of a possible :: notation by splitting the block into : separated IPv6 address groups set blk1 "" foreach grp [split [getfield $addr "::" 1] ":"] { # Check if current group contains a IPv4 address notation if { $grp contains "." } then { # The current group contains a IPv4 address notation. Trying to extract the four IPv4 address octets scan $grp {%d.%d.%d.%d} oct1 oct2 oct3 oct4 # Convert the four IPv4 address octets into two IPv6 address groups by querying the $static::ipv6_dec_map array append blk1 "$static::ipv6_dec_map($oct1)$static::ipv6_dec_map($oct2) $static::ipv6_dec_map($oct3)$static::ipv6_dec_map($oct4) " set oct4 "" } else { # The current group contains just a IPv6 address notation. Filling up the IPv6 address group with leading zeros by querying the $static::ipv6_grp_filler array append blk1 "$static::ipv6_grp_filler([string length $grp])$grp " } } # Parsing the second IPv6 address block of a possible :: notation by splitting the block into : IPv6 address separated groups set blk2 "" foreach grp [split [getfield $addr "::" 2] ":"] { # Check if current group contains a IPv4 address notation if { $grp contains "." } then { # The current group contains a IPv4 address notation. Trying to extract the four IPv4 address octets scan $grp {%d.%d.%d.%d} oct1 oct2 oct3 oct4 # Convert the four IPv4 address octets into two IPv6 address groups by querying the $static::ipv6_dec_map array append blk2 "$static::ipv6_dec_map($oct1)$static::ipv6_dec_map($oct2) $static::ipv6_dec_map($oct3)$static::ipv6_dec_map($oct4) " set oct4 "" } else { # The current group contains just a IPv6 address notation. Filling up the IPv6 address group with leading zeros by querying the $static::ipv6_grp_filler array append blk2 "$static::ipv6_grp_filler([string length $grp])$grp " } } # Joining the first and second block of the possible :: notation while expanding the address to 128bit length by querying the $static::ipv6_addr_filler array set addr "[join "$blk1$static::ipv6_addr_filler([string length "$blk1$blk2"]) $blk2" ":"]" }] } then { # log local0.debug "errorInfo: [subst \$::errorInfo]" # return "errorInfo: [subst \$::errorInfo]" return "" } # Append the previously extracted IPv6 ZoneID / Route Domain suffix and return the expanded IPv6 address notation return "$addr$id" } Tested this on version: 12.0913Views0likes2Comments