on 18-Mar-2015 13:00
Problem this snippet solves:
This is a solution that allows client from IPv6 network to communicate to IPv4 network thru BIG-IP. It actually contains 2 iRules:- NAT64 and DNS64 iRule (see iRule source section below).
Here is how it works:-
Change in version 2:-
Please be noted. This iRule is still in experimental stage and it may contains redundant routine. Any comment/feedback is welcome.
Code :
# virtual server configuration virtual dns64 { pool dns64 destination 2001:123::1.domain ip protocol udp rules dns64 } virtual nat64 { translate address enable snat automap destination 2002:123::.any mask ffff:ffff:ffff:ffff:ffff:ffff:: rules nat64 } # Pool for DNS server in IPv4 domain pool dns64 { members 172.27.4.209:domain {} } # NAT64 iRule when CLIENT_ACCEPTED { node [string range [IP::addr [IP::local_addr] mask ::ffff:ffff] 0 end] } # DNS64 iRule when RULE_INIT { set static::prefix "200201230000000000000000" # timeout in milliseconds set static::timeout 2000 set static::type_a [binary format S 1] set static::type_aaaa [binary format S 28] } when CLIENT_DATA { # first send AAAA as is set original_request [UDP::payload] set after_id [ after $static::timeout { binary scan ${original_request} SSSSSS id flags qdcount ancount nscount arcount # Total Header length = 12 bytes set index 12 # Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually. # also assume no pointer (dns compression) here while { $qdcount > 0 } { binary scan ${original_request} @${index}c count while { $count != 0 } { incr index [expr $count + 1] binary scan ${original_request} @${index}c count } incr index binary scan ${original_request} @${index}SS qtype qclass # change to A record if { $qtype == 28 } { set modified_request [binary format a* \ [string replace ${original_request} ${index} [expr ${index} + 1] ${static::type_a} ]] } incr index 4 incr qdcount -1 } serverside { UDP::respond ${modified_request} } unset original_request } ] } when SERVER_DATA { # cancel after id (server responds before timeout after cancel $after_id if { [info exists original_request] } { # assume this is a response to AAAA query binary scan [UDP::payload] SSSSSS id flags qdcount ancount nscount arcount set rcode [ expr $flags & 0xf ] if { $rcode == 3 } { # forward error response to client return } # Total Header length = 12 bytes set index 12 # Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually. while { $qdcount > 0 } { binary scan [UDP::payload] @${index}c count while { $count != 0 } { incr index [expr $count + 1] binary scan [UDP::payload] @${index}c count } incr index binary scan [UDP::payload] @${index}SS qtype qclass # change A(1) to AAAA(28) record if { $qtype == 1 } { UDP::payload replace ${index} 2 ${static::type_aaaa} } incr index 4 incr qdcount -1 } # The Answer, Authority and Additional Sections while { $ancount > 0 } { binary scan [UDP::payload] @${index}cc count pointer set loop 0 while { $count != 0 and $loop < 30 } { incr loop set pointer_prefix [expr ($count >> 6) & 0x3] set pointer_index [expr (($count &0x3f)<<8) | $pointer ] if { $pointer_prefix == 3 } { incr index 2 break } else { incr index [expr $count + 1] binary scan [UDP::payload] @${index}cc count pointer } } binary scan [UDP::payload] @${index}SSIS qtype qclass ttl rdlength incr index 10 if { $qtype == 28 } { # forward dns error to client log local0.alert "forward as is 2" return } incr index $rdlength if { $ancount > 0 } { incr ancount -1 } elseif { $nscount > 0 } { incr nscount -1 } else { incr arcount -1 } } # when server return RCODE other than 3 with no AAAA answer, send A query binary scan ${original_request} SSSSSS id flags qdcount ancount nscount arcount # Total Header length = 12 bytes set index 12 # Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually. # also assume no pointer (dns compression) here while { $qdcount > 0 } { binary scan ${original_request} @${index}c count while { $count != 0 } { incr index [expr $count + 1] binary scan ${original_request} @${index}c count } incr index binary scan ${original_request} @${index}SS qtype qclass # change to A record if { $qtype == 28 } { set modified_request [binary format a* \ [string replace ${original_request} ${index} [expr ${index} + 1] ${static::type_a} ]] } incr index 4 incr qdcount -1 } if { [info exists modified_request] } { UDP::respond ${modified_request} unset original_request UDP::drop } } else { # assume this is a response to A query set a_index_list "" binary scan [UDP::payload] SSSSSS id flags qdcount ancount nscount arcount # Total Header length = 12 bytes set index 12 # Question Section: it is usually 1 (qdcount=1), we may not need to loop here actually. while { $qdcount > 0 } { binary scan [UDP::payload] @${index}c count while { $count != 0 } { incr index [expr $count + 1] binary scan [UDP::payload] @${index}c count } incr index binary scan [UDP::payload] @${index}SS qtype qclass # change A(1) to AAAA(28) record if { $qtype == 1 } { UDP::payload replace ${index} 2 ${static::type_aaaa} } elseif { $qtype == 28 } { # answer to AAAA is not expected at this stage, drop it UDP::drop return } incr index 4 incr qdcount -1 } # The Answer, Authority and Additional Sections while { $ancount > 0 || $nscount > 0 || $arcount > 0} { binary scan [UDP::payload] @${index}cc count pointer set loop 0 while { $count != 0 and $loop < 30 } { incr loop set pointer_prefix [expr ($count >> 6) & 0x3] set pointer_index [expr (($count &0x3f)<<8) + ($pointer & 0xff) ] if { $pointer_prefix == 3 } { set save_pointer $pointer_index foreach a $a_index_list { if { $pointer_index < $a } { break } incr pointer_index 12 } # rewrite DNS compression pointer to appropriate value if { $pointer_index > $save_pointer } { UDP::payload replace $index 2 [binary format S [expr $pointer_index | 0xc000]] } incr index 2 break } else { incr index [expr $count + 1] binary scan [UDP::payload] @${index}cc count pointer } } # if it is root record if { $loop == 0 } { incr index } binary scan [UDP::payload] @${index}SSIS qtype qclass ttl rdlength incr index 10 # only modify answer section if { $qtype == 1 && $ancount > 0 } { lappend a_index_list $index # change A to AAAA record binary scan [UDP::payload] @${index}cccc a b c d # change length from 4 to 6 set rdlength 16 # change IPv4 to IPv6 UDP::payload replace [expr ${index} - 10] 14 \ [binary format SSISH24cccc 28 $qclass $ttl $rdlength $static::prefix $a $b $c $d] } elseif { $qtype == 2 } { # rewrite dns compression in RDATA # add more condition here (now it only check for qtype=2 (NS) binary scan [UDP::payload] @${index}a${rdlength} rdata for { set x 0 } { $x < [expr $rdlength - 1] } { incr x } { binary scan $rdata x${x}cc count pointer set pointer_prefix [expr ($count >> 6) & 0x3] set pointer_index [expr (($count &0x3f)<<8) + ($pointer & 0xff) ] if { $pointer_prefix == 3 } { set save_pointer $pointer_index foreach a $a_index_list { if { $pointer_index < $a } { break } incr pointer_index 12 } # rewrite DNS compression pointer to appropriate value if { $pointer_index > $save_pointer } { UDP::payload replace [expr $index + $x] 2 [binary format S [expr $pointer_index | 0xc000]] } } } } incr index $rdlength if { $ancount > 0 } { incr ancount -1 } elseif { $nscount > 0 } { incr nscount -1 } else { incr arcount -1 } } } }
Hi , Is it possible to handle the same scenario ( allows client from IPv6 network to communicate to IPv4 network thru BIG-IP ) in LTM without iRule ? I mean by creating a DNS profile and activating "DNS IPv6 to IPv4" = Secondary and "IPv6 to IPv4 Additional Section Rewrite" = Any in it and assing it to IPV4.0 virtual server ?