#!/bin/sh ### Generate a list of iptables rules to redirect port 53 traffic (tcp+udp) and echo to console. ### This version of the script does NOT apply anything to the iptables on the machine it is run on. ### IPv4 DNS traffic is redirected to $PRIMARYDNS_IPV4 on port 53 ### IPv6 DNS traffic is redirected to $PRIMARYDNS_IPV6 on port 53 # This is the DNS that you want clients to be forced to use on your network # If you have a secondary DNS, add it to the whitelist below and it will still work. PRIMARYDNS_IPV4="192.168.1.12" # Add ALL your whitelisted DNS servers to this variable, separated by a space # These are any name servers that will not be rerouted to the primary DNS. # Recommend you include your router address so that DNS for local machines will still work. # if left blank (""), PRIMARYDNS_IPV4 will still be whitelisted. # e.g. "192.168.1.1 192.168.1.2" etc. DNSWHITELIST_IPV4="192.168.1.12 192.168.1.1" # Leave this blank if you don't use IPv6. PRIMARYDNS_IPV6="" # Add ALL your whitelisted DNS servers to this variable, separated by a space. # In my experience it is best practice to include every ipv6 address that each # DNS server uses. I have observed packets sent to my pi-hole by all 3 ipv6 addresses it obtains # even though only one is listed in DHCP. # e.g. "ffff::1234 ffff:ffff:ffff:ffff::5678" etc. DNSWHITELIST_IPV6="" # Set this to add exception mac addresses that will not be forced to use the primary DNS server # Separate MAC addresses with spaces. EXCEPTION_MACS="" # Set this to the interfaces you want to force through the DNS IP. # Separate interfaces with spaces. # on most routers this is br0. # if you have multiple subnets you might need to add more bridge interfaces to this list. # e.g. "br0" or "br0 br1" etc. FORCED_INTFC="br0" # Enable this if your pihole is on the same subnet as the devices/interfaces # you are forwarding. This will allow the return traffic to work on the same # subnet, but will make the pihole think the requests are coming from the router. # You will lose client information in the pihole dashboard if you enable this. # It is preferable to put the pihole on a different subnet and disable this. ENABLE_MASQUERADE=1 ###IPV4 SECTION### #using ipsets to make whitelists lets us limit wasteful rerouting and reduce potential for DNS loops # create dnsv4 ipset echo "ipset create dnsv4 hash:ip family inet" echo "ipset add dnsv4 ${PRIMARYDNS_IPV4}" if [ -n "${DNSWHITELIST_IPV4}" ]; then for ip in ${DNSWHITELIST_IPV4}; do echo "ipset add dnsv4 ${ip}" done fi #create new DNSCONTROL chain with exceptions echo "iptables -t nat -N DNSCONTROL" #add exceptions that won't get forcibly routed - their DNS requests get routed based on their internal settings. if [ -n "${EXCEPTION_MACS}" ]; then for mac in ${EXCEPTION_MACS}; do dnscontrol_rule="DNSCONTROL -m mac --mac-source ${mac} -j RETURN" echo "iptables -t nat -A ${dnscontrol_rule}" done fi #Everything else is DNAT'd into the pihole. for proto in tcp udp; do dnscontrol_rule="DNSCONTROL -p ${proto} -j DNAT --to ${PRIMARYDNS_IPV4}:53" echo "iptables -t nat -A ${dnscontrol_rule}" done # rules to push DNS traffic into DNSCONTROL chain for intfc in ${FORCED_INTFC}; do for proto in tcp udp; do prerouting_rule="PREROUTING -i ${intfc} -p ${proto} --dport 53 -m set ! --match-set dnsv4 src -m set ! --match-set dnsv4 dst -j DNSCONTROL" echo "iptables -t nat -A ${prerouting_rule}" done done #MASQUERADE rules for ipv4 if [ "$ENABLE_MASQUERADE" = "1" ]; then for proto in udp tcp; do postrouting_rule="POSTROUTING -p ${proto} --dport 53 -m set ! --match-set dnsv4 src -m set --match-set dnsv4 dst -j MASQUERADE" echo "iptables -t nat -A ${postrouting_rule}" done fi ###IPV6 SECTION### # This section is skipped entirely if PRIMARYDNS_IPV6 is not set! if [ -n "${PRIMARYDNS_IPV6}" ]; then # create dnsv6 ipset echo "ipset create dnsv6 hash:ip family inet6" echo "ipset add dnsv6 ${PRIMARYDNS_IPV6}" if [ -n "${DNSWHITELIST_IPV6}" ]; then for ip in ${DNSWHITELIST_IPV6}; do echo "ipset add dnsv6 ${ip}" done fi # create new DNSCONTROL chain with exceptions echo "ip6tables -t nat -N DNSCONTROL" # add exceptions that won't get forcibly routed - their DNS requests get routed based on their internal settings. if [ -n "${EXCEPTION_MACS}" ]; then for mac in ${EXCEPTION_MACS}; do dnscontrol_rule="DNSCONTROL -m mac --mac-source ${mac} -j RETURN" echo "ip6tables -t nat -A ${dnscontrol_rule}" done fi # Everything else is DNAT'd into the pihole. for proto in tcp udp; do dnscontrol_rule="DNSCONTROL -p ${proto} -j DNAT --to [${PRIMARYDNS_IPV6}]:53" echo "ip6tables -t nat -A ${dnscontrol_rule}" done # rules to push DNS traffic into DNSCONTROL chain for intfc in ${FORCED_INTFC}; do if [ -d "/sys/class/net/${intfc}" ]; then for proto in tcp udp; do prerouting_rule="PREROUTING -i ${intfc} -p ${proto} --dport 53 -m set ! --match-set dnsv6 src -m set ! --match-set dnsv6 dst -j DNSCONTROL" echo "ip6tables -t nat -A ${prerouting_rule}" done fi done # MASQUERADE rules for ipv4 if [ "$ENABLE_MASQUERADE" = "1" ]; then for proto in udp tcp; do postrouting_rule="POSTROUTING -p ${proto} --dport 53 -m set ! --match-set dnsv6 src -m set --match-set dnsv6 dst -j MASQUERADE" echo "ip6tables -t nat -A ${postrouting_rule}" done fi fi