Forum Discussion
iRules code share,only use tcp protocol profile to log tcp dns request and A or AAAA dns answers ip
if you not have GTM/DNS license, VS in Standard mode, only use tcp protocol profile to log tcp dns request, and if query type is A or AAAA, it will also log A or AAAA dns answers ip
proc decode_dns_package {headername result {dns_protocol_type "udp"}} {
if { $dns_protocol_type eq "tcp" } {
set result [string range $result 2 end]
}
binary scan $result @6H4 answer_RRS
set answer_count [scan $answer_RRS {%x}]
for {set rr 0; set answer_data_length 0; set list_hex_ip {}} {$rr < $answer_count} {incr rr} {
set catch_result [binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 2}]H4 hex_answer_type]
if { $catch_result == 1 } {
set answer_type [scan $hex_answer_type {%x}]
if { $answer_type == 28 } {
binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 12}]H32 hex_ip
lappend list_hex_ip $hex_ip
incr answer_data_length 16
set dns_type_aaaa 1
} elseif { $answer_type == 1 } {
binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 12}]H8 hex_ip
lappend list_hex_ip $hex_ip
incr answer_data_length 4
set dns_type_a 1
} else {
binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 10}]H4 hex_data_length
incr answer_data_length [scan $hex_data_length {%x}]
}
}
}
set dns_answer_list {}
if { [llength $list_hex_ip] == 0 } {
set dns_answer_list {}
} else {
if { [info exists dns_type_aaaa] } {
foreach dns_aaaa_ipv6 $list_hex_ip {
for {set i 1; set aaaa_ipv6 [string range $dns_aaaa_ipv6 0 3]} { $i <= 7 } {incr i} {
append aaaa_ipv6 ":[string range $dns_aaaa_ipv6 [expr {$i * 4}] [expr {$i * 4 + 3}]]"
}
set compress_ipv6 [IP::addr $aaaa_ipv6 mask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]
lappend dns_answer_list $compress_ipv6
}
} else {
foreach dns_a_ip $list_hex_ip {
scan $dns_a_ip {%2x%2x%2x%2x} ip1 ip2 ip3 ip4
set a_ipv4 "$ip1.$ip2.$ip3.$ip4"
lappend dns_answer_list $a_ipv4
}
}
}
return $dns_answer_list
}
proc decode_dns_query_name {dns_query_post {dns_protocol_type "udp"}} {
if { $dns_protocol_type eq "udp" } {
set dns_query_post $dns_query_post
} else {
set dns_query_post [string range $dns_query_post 2 end]
}
for {set i 0;set offset 12;set length 1;set endlength 1;set query_name ""} { $length > 0 && $i < 10 } { incr i } {
binary scan [string range $dns_query_post $offset $offset] c foo
set length [expr {$foo & 0xff}]
if { $length > 0 } {
append query_name [string range $dns_query_post [expr {$offset + 1}] [expr {$offset + $length}]]
set offset [expr {$offset + $length + 1}]
binary scan [string range $dns_query_post $offset $offset] c foo
set endlength [expr {$foo & 0xff}]
if { $endlength > 0 } {
append query_name "."
}
}
}
return $query_name
}
when RULE_INIT {
array set static::dns_type_array {
1 "A"
28 "AAAA"
5 "CNAME"
2 "NS"
6 "SOA"
12 "PTR"
15 "MX"
16 "TXT"
33 "SRV"
35 "NAPTR"
10 "NULL"
46 "RRSIG"
48 "DNSKEY"
52 "TLSA"
65 "HTTPS"
251 "IXFR"
252 "AXFR"
255 "ANY"
256 "URI"
257 "CAA"
}
}
when CLIENT_ACCEPTED priority 500 {
TCP::collect
}
when CLIENT_DATA priority 500 {
set client_dns_query_packet [TCP::payload]
set dns_query_name [call decode_dns_query_name $client_dns_query_packet "tcp"]
# set header_name_length [expr {12 + 1 + [string length $dns_query_name] + 1 + 2 + 2}]
binary scan $client_dns_query_packet @2H4 hex_dns_query_id
binary scan $client_dns_query_packet @[expr {2 + 12 + 1 + [string length $dns_query_name] + 1}]H4 hex_dns_type
set dns_question_type [lindex [array get static::dns_type_array [scan $hex_dns_type {%x}]] 1]
if { $dns_question_type eq "A" or $dns_question_type eq "AAAA" } {
set answer_decode_action 1
}
log local0. "client ip is [IP::client_addr], dns question name is $dns_query_name, query id is [scan $hex_dns_query_id {%x}], query type is $dns_question_type"
TCP::release
}
when SERVER_CONNECTED priority 500 {
if { [info exists answer_decode_action] } {
set collect_server_data 1
TCP::collect
} else {
return
}
}
when SERVER_DATA priority 500 {
if { [info exists collect_server_data] } {
set dns_answer_packet [TCP::payload]
if { [string length $dns_answer_packet] > 12 } {
# log local0. "haha"
binary scan $dns_answer_packet @4H4 rcode
set replay_code [scan [string range $rcode end end] {%x}]
# log local0. "replay_code is $replay_code"
if { $replay_code == 0 } {
set header_name_length [expr {12 + 1 + [string length $dns_query_name] + 1 + 2 + 2}]
set headername [TCP::payload 2 $header_name_length]
# log local0. "length is [string length $headername]"
set tcp_answer_result [call decode_dns_package $headername $dns_answer_packet "tcp"]
log local0. "client ip is [IP::client_addr], dns query id is [scan $hex_dns_query_id {%x}], dns question name is $dns_query_name, dns query type is $dns_question_type, dns answer is $tcp_answer_result"
} else {
log local0. "dns answer is empty, because dns replay_code is $replay_code"
}
}
TCP::release
} else {
return
}
}
iRules log out:
Oct 14 13:52:41 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <CLIENT_DATA>: client ip is 10.1.233.100, dns question name is www.xuwen.com, query id is 45281, query type is A
Oct 14 13:52:41 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <SERVER_DATA>: client ip is 10.1.233.100, dns query id is 45281, dns question name is www.xuwen.com, dns query type is A, dns answer is 6.6.6.6 188.100.100.100 114.188.166.166
Oct 14 13:52:49 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <CLIENT_DATA>: client ip is 10.1.233.100, dns question name is www.xuwen.com, query id is 21225, query type is AAAA
Oct 14 13:52:49 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <SERVER_DATA>: client ip is 10.1.233.100, dns query id is 21225, dns question name is www.xuwen.com, dns query type is AAAA, dns answer is 240e:698::114:188:166:166 240e:698::188:100:100:100 240e:2698::8:88:888:8888
Oct 14 13:52:52 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <CLIENT_DATA>: client ip is 10.1.233.100, dns question name is www.gslb.xuwen.com, query id is 19836, query type is A
Oct 14 13:52:52 f5 info tmm[10881]: Rule /Common/LTMTCP_DNS_check <SERVER_DATA>: client ip is 10.1.233.100, dns query id is 19836, dns question name is www.gslb.xuwen.com, dns query type is A, dns answer is 58.213.97.84
Nice addition, xuwen, thanks for sharing!
For those that find this, it's a fantastic example of how to use binary commands to decode the protocol, but I wouldn't recommend logging dns traffic to local0. unless it's for a very brief point in time for troubleshooting purposes, and even then, if your system has heavy dns traffic, it's likely to significantly reduce throughput. A better option for logging from iRules would be to use HSL and send the logs off-box for analysis.
- JRahmAdmin
Nice addition, xuwen, thanks for sharing!
For those that find this, it's a fantastic example of how to use binary commands to decode the protocol, but I wouldn't recommend logging dns traffic to local0. unless it's for a very brief point in time for troubleshooting purposes, and even then, if your system has heavy dns traffic, it's likely to significantly reduce throughput. A better option for logging from iRules would be to use HSL and send the logs off-box for analysis.
- xuwenCumulonimbus
proc decode_dns_package {headername result {dns_protocol_type "udp"}} { if { $dns_protocol_type eq "tcp" } { set result [string range $result 2 end] } binary scan $result @6H4 answer_RRS set answer_count [scan $answer_RRS {%x}] # set list_hex_ip {} for {set rr 0; set answer_data_length 0; set list_hex_ip {}} {$rr < $answer_count} {incr rr} { set catch_result [binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 2}]H4 hex_answer_type] if { $catch_result == 1 } { set answer_type [scan $hex_answer_type {%x}] if { $answer_type == 28 } { binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 12}]H32 hex_ip lappend list_hex_ip $hex_ip incr answer_data_length 16 set dns_type_aaaa 1 } elseif { $answer_type == 1 } { binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 12}]H8 hex_ip lappend list_hex_ip $hex_ip incr answer_data_length 4 set dns_type_a 1 } else { binary scan $result @[expr {[string length $headername] + 12 * $rr + $answer_data_length + 10}]H4 hex_data_length incr answer_data_length [scan $hex_data_length {%x}] } } } set dns_answer_list {} if { [llength $list_hex_ip] == 0 } { set dns_answer_list {} } else { if { [info exists dns_type_aaaa] } { foreach dns_aaaa_ipv6 $list_hex_ip { for {set i 1; set aaaa_ipv6 [string range $dns_aaaa_ipv6 0 3]} { $i <= 7 } {incr i} { append aaaa_ipv6 ":[string range $dns_aaaa_ipv6 [expr {$i * 4}] [expr {$i * 4 + 3}]]" } set compress_ipv6 [IP::addr $aaaa_ipv6 mask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff] lappend dns_answer_list $compress_ipv6 } } else { foreach dns_a_ip $list_hex_ip { scan $dns_a_ip {%2x%2x%2x%2x} ip1 ip2 ip3 ip4 set a_ipv4 "$ip1.$ip2.$ip3.$ip4" lappend dns_answer_list $a_ipv4 } } } return $dns_answer_list } proc decode_dns_query_name {dns_query_post {dns_protocol_type "udp"}} { if { $dns_protocol_type eq "udp" } { set dns_query_post $dns_query_post } else { set dns_query_post [string range $dns_query_post 2 end] } for {set i 0;set offset 12;set length 1;set endlength 1;set query_name ""} { $length > 0 && $i < 10 } { incr i } { binary scan [string range $dns_query_post $offset $offset] c foo set length [expr {$foo & 0xff}] if { $length > 0 } { append query_name [string range $dns_query_post [expr {$offset + 1}] [expr {$offset + $length}]] set offset [expr {$offset + $length + 1}] binary scan [string range $dns_query_post $offset $offset] c foo set endlength [expr {$foo & 0xff}] if { $endlength > 0 } { append query_name "." } } } return $query_name } when RULE_INIT { array set static::dns_type_array { 1 "A" 28 "AAAA" 5 "CNAME" 2 "NS" 6 "SOA" 12 "PTR" 15 "MX" 16 "TXT" 33 "SRV" 35 "NAPTR" 10 "NULL" 46 "RRSIG" 48 "DNSKEY" 52 "TLSA" 65 "HTTPS" 251 "IXFR" 252 "AXFR" 255 "ANY" 256 "URI" 257 "CAA" } } when CLIENT_ACCEPTED priority 500 { set hsl [HSL::open -proto UDP -pool syslog_pool] TCP::collect } when CLIENT_DATA priority 500 { set client_dns_query_packet [TCP::payload] set dns_query_name [call decode_dns_query_name $client_dns_query_packet "tcp"] # set header_name_length [expr {12 + 1 + [string length $dns_query_name] + 1 + 2 + 2}] binary scan $client_dns_query_packet @2H4 hex_dns_query_id binary scan $client_dns_query_packet @[expr {2 + 12 + 1 + [string length $dns_query_name] + 1}]H4 hex_dns_type set dns_question_type [lindex [array get static::dns_type_array [scan $hex_dns_type {%x}]] 1] if { $dns_question_type eq "A" or $dns_question_type eq "AAAA" } { set answer_decode_action 1 } # log local0. "client ip is [IP::client_addr], dns question name is $dns_query_name, query id is [scan $hex_dns_query_id {%x}], query type is $dns_question_type" HSL::send $hsl "client ip is [IP::client_addr], dns question name is $dns_query_name, query id is [scan $hex_dns_query_id {%x}], query type is $dns_question_type" TCP::release } when SERVER_CONNECTED priority 500 { if { [info exists answer_decode_action] } { set collect_server_data 1 TCP::collect } else { return } } when SERVER_DATA priority 500 { if { [info exists collect_server_data] } { set dns_answer_packet [TCP::payload] if { [string length $dns_answer_packet] > 12 } { # log local0. "haha" binary scan $dns_answer_packet @4H4 rcode set replay_code [scan [string range $rcode end end] {%x}] # log local0. "replay_code is $replay_code" if { $replay_code == 0 } { set header_name_length [expr {12 + 1 + [string length $dns_query_name] + 1 + 2 + 2}] set headername [TCP::payload 2 $header_name_length] # log local0. "length is [string length $headername]" set tcp_answer_result [call decode_dns_package $headername $dns_answer_packet "tcp"] # log local0. "client ip is [IP::client_addr], dns query id is [scan $hex_dns_query_id {%x}], dns question name is $dns_query_name, dns query type is $dns_question_type, dns answer is $tcp_answer_result" HSL::send $hsl "client ip is [IP::client_addr], dns query id is [scan $hex_dns_query_id {%x}], dns question name is $dns_query_name, dns query type is $dns_question_type, dns answer is $tcp_answer_result" } else { # log local0. "dns answer is empty, because dns replay_code is $replay_code" HSL::send $hsl "dns answer is empty, because dns replay_code is $replay_code" } } TCP::release } else { return } }
iRules has make a little modifications:
syslog local0. has been modified to HSL
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com