iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to 10.0.0.2:53
iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to 10.0.0.2:53
The OUTPUT chain is only for packets that originate on the host where the iptables rule is running; i.e. the router/firewall itself. If you want to catch packets that originated elsewhere and are being forwarded by the router/firewall, you want the FORWARD chain.
But the NAT table doesn't have a FORWARD chain, because forwarding doesn't make sense in the context of NAT. What the NAT table does have is the PREROUTING chain, which is the preferred way to do destination NATing. So the proper rule is
iptables -t nat -A PREROUTING [source addr/iface specifiers here] -p udp --dport domain -j DNAT --to-destination 10.0.0.2
If you want to do stochastic load-balancing between 10.0.0.2 and
10.0.0.3:
iptables -t nat -A PREROUTING [source addr/iface specifiers here] -p udp --dport domain -j DNAT --to-destination 10.0.0.2 -m statistic --mode random --probability 0.5
iptables -t nat -A PREROUTING [source addr/iface specifiers here] -p udp --dport domain -j DNAT --to-destination 10.0.0.3
If you want to log whenever a host tries to use their own DNS servers, add these lines before the DNAT lines:
iptables -t nat -A PREROUTING [source addr/iface specifiers here] \! -d
10.0.0.0/30 -p udp --dport domain -j LOG
I'd highly recommend adding some rate limiting to the LOG rules with "-m limit --limit 4/hour" or something like that. Logging every attempt to circumvent the DNS would, I think, rapidly generate an overwhelming and unnecessary amount of info. Rate-limiting will still tell you what host is doing it.
n.b.: I've left handling of DNS's TCP traffic as an exercise for the reader.