2

How to drop incoming negative DNS responses in Linux? (I want a timeout instead) I'm thinking of creating an iptables rule:

iptables -I INPUT -p udp --sport 53 -m u32 ...

From wireshark:

000E start of IP Packet (fixed, at first, I wrote 0010)
0022 start of UDP Packet
002A start of DNS message
002C  Flags
        .... .... .... 0000 = Reply code: No error (0)
        .... .... .... 0011 = Reply code: No such name (3)

I think the offset for -m u32 is counted from the ip header. Also subtracting 2 to get a 32bit.

$ echo $(( 0x002C - 0x000E - 2 ))
28

The mask for the last 4 bits is 0xF
So the rule should be something like this:

iptables -I INPUT -p udp --sport 53 -m u32 --u32 "28&0xF=3" -j DROP
basin
  • 558
  • 1
  • 5
  • 22

4 Answers4

5

Offset for DNS flags in ip packet is 28bytes and you need to check last 2 bits in 2byte field u32 rule for that is "28&0x000F=0x03"

My test rule is: iptables -A INPUT -m u32 -p udp --sport 53 --u32 "28&0x000F=0x03" -j LOG

BTW I like the idea to use this firewall rule for split horizon DNS, going to test it for some of my networks.

DukeLion
  • 3,259
  • 1
  • 18
  • 19
2

@DukeLion is correct, but you should also be aware of NOERROR but NOANSWER. To drop this too, add this iptables rule:

iptables -I INPUT -m u32 -p udp --sport 53 --u32 "32&0xFFFF=0x0000" -j DROP

PS. DNS RCODE in IP packet "real" offset is 31.5 bytes, so I don't like matching it use 0x1e&0x30000=0x30000

Andrew Schulman
  • 8,811
  • 21
  • 32
  • 47
imoc
  • 53
  • 5
  • To make it more robust, you can use it with `0x1b&0x80=0x80` to indicate this is a response, but since --sport 53 already implied this, so this is actually not needed. – imoc Feb 04 '21 at 06:37
2

This drops BIND9 "rejected" replies to the current spoofed UDP queries!

iptables -F OUTPUT 
#iptables -A OUTPUT -p udp --sport 53 -j LOG --log-prefix="OUT-UPD-RAW : "
#iptables -A OUTPUT -m u32 -p udp --sport 53 --u32 "28&0xFFFF=0x8105" -j LOG --log-prefix="STOPPED-DNS-REJECTED-REPLY "
iptables -A OUTPUT -m u32 -p udp --sport 53 --u32 "28&0xFFFF=0x8105" -j DROP
iptables -A OUTPUT -p udp --sport 53 -j LOG --log-prefix="OUT-UPD-SENT: "
user213378
  • 21
  • 1
1

Dropping negative responses worked fine until I added more than one search domain to /etc/resolv.conf: libc tries the suffixes sequentially and if I drop the negative responses for the wrong suffix initially picked by the libc resolver it gives up with a timeout before it tries the right suffix.

So I decided to delay the negative responses instead of dropping them. This makes sure dnsmasq gets the positive response from the right server before it gets the negative responses from the other servers.

Delaying is done not with iptables, but with iproute2. Here's my configuration for a modern systemd Linux:

/etc/modules-load.d/my-tc.conf

ifb

/etc/systemd/system/my-tc.service

[Unit]
Before=NetworkManager.service

[Service]
Type=oneshot
ExecStart=/bin/bash -c "/usr/bin/ip link set dev ifb0 up && /usr/bin/tc qdisc add dev ifb0 root netem delay 900ms"
ExecStop=/bin/bash -c "/usr/bin/tc qdisc del dev ifb0 root && /usr/bin/ip link set dev ifb0 down"
RemainAfterExit=true

[Install]
WantedBy=network.target

/etc/NetworkManager/dispatcher.d/my-tc.sh

#!/bin/bash
if [ "enp0s3 up" = "$1 $2" ]; then
  tc qdisc add dev enp0s3 ingress
  tc filter add dev enp0s3 parent ffff: protocol ip \
    u32 \
      match ip protocol 17 0xff \
      match ip sport 53 0xffff \
      match u32 0x03 0x0F at 28 \
    flowid 1:1 action mirred egress redirect dev ifb0
fi
basin
  • 558
  • 1
  • 5
  • 22