I have iptables blocking all UDP traffic at the moment, however I want to allow only certain DNS queries to get through.

Let's use google.com as an example.

I am trying to use string matching to find the domain name in the request, and allow it. This is what I came up with.

iptables -A OUTPUT -o eth0 -p udp --sport 53 -m string --string "google.com" --algo bm -j ACCEPT

I have also tried --dport 53 instead of --sport. No dice.

If anyone knows how this can be done or see's where I went wrong?

  • Post the output of iptables -L -n -v
    – dmourati
    Dec 31, 2012 at 6:43
  • Why dont you give access to only required authoritative DNS servers ?? i.e. ns[1-4].google.com in this case Dec 31, 2012 at 7:11
  • @Jarred Kenny: This usage of string matching in iptables is not accurate. Why do you need to do this? Are you trying to block some websites?
    – Khaled
    Dec 31, 2012 at 8:14
  • @Khaled The server connects to a VPN at book. I am writing these rules to prevent any DNS queries being sent over the regular eth0. This rule is an attempt to only allow the DNS queries related to the VPN provider. For example, I only want DNS queries to resolve for *.vpncompany.com domain names. Once the VPN is connected, DNS queries get piped via the tun- interface and the rule is not relevant. It is just a precaution. Dec 31, 2012 at 12:20
  • 3
    You'd probably be better off installing a local DNS server on the machine with selective forwarding. E.g. disable recursion except for zone vpncompany.com. Dec 31, 2012 at 18:59

3 Answers 3


the dot "." in a DNS query is not represented as a character, but as the length of the string that follows. For example www.google.com is queried as

0x03 w w w 0x06 g o o g l e 0x03 c o m

you can easily allow/block DNS queries by matching the domain names with --hex-string. In your case:

-m string --algo bm --hex-string '|06 676f6f676c65 03 636f6d|' -j ACCEPT

will accept every DNS packet containing ".google.com".

I often use this technique against the DNS query amplification attack.

source: DNS RFC 1035


To complement nrc's anwser, where is a quick command to convert domains to the hexadecimal string:

perl -e 'print map {chr(length($_)).$_} split /\./, "'$DOMAIN'" | xxd -p

So, in your case:

HEX=$(perl -e 'print map {chr(length($_)).$_} split /\./, "'$DOMAIN'"' | xxd -p)

iptables -A OUTPUT -o eth0 -p udp --sport 53 \
  -m string --hex-string "|$HEX|" --algo bm -j ACCEPT
str=$(perl -e 'print "|".join("|", map { sprintf("%02X", length($_))."|".$_ }  split(/\./, "'$DOMAIN'") )."|"')
echo $str
iptables -I INPUT -p udp --dport 53 -m string --hex-string "$str" --algo bm -j ACCEPT


iptables -I INPUT -p udp --dport 53 -m string --hex-string "$str" --algo bm -j ACCEPT

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.