Many sysadmins encounter issues when trying to use the negation operator (!) in iptables destination rules. The common error message:
iptables -A OUTPUT -d ! 134.134.134.134 -j ACCEPT
Bad argument 134.134.134.134'
stems from incorrect syntax placement rather than any problem with the IP address itself.
In iptables, the negation operator must be placed before the parameter flag, not after it. The correct format:
iptables -A OUTPUT ! -d 134.134.134.134 -j ACCEPT
Here's how to properly implement DNS traffic redirection to a sinkhole while excluding specific DNS servers:
# Allow traffic to our preferred DNS
iptables -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j ACCEPT
# Redirect all other DNS traffic to sinkhole
iptables -A OUTPUT -p udp --dport 53 ! -d 8.8.8.8 -j DNAT --to-destination 10.0.0.254:53
You can combine multiple negations for complex firewall rules. Example excluding both Google and Cloudflare DNS:
iptables -A OUTPUT -p udp --dport 53 \
! -d 8.8.8.8 \
! -d 1.1.1.1 \
-j DNAT --to-destination 10.0.0.254:53
- Always test rules with iptables -L -v -n before applying
- The negation applies only to the immediately following parameter
- For complex rules, consider using ipset for better performance
For large-scale exclusions, ipset provides better performance:
# Create ipset
ipset create allowed-dns hash:ip
# Add allowed DNS servers
ipset add allowed-dns 8.8.8.8
ipset add allowed-dns 1.1.1.1
# iptables rule using ipset
iptables -A OUTPUT -p udp --dport 53 -m set ! --match-set allowed-dns dst -j DNAT --to-destination 10.0.0.254:53
When working with iptables, many administrators encounter syntax confusion when trying to use the negation operator (!) for destination addresses. The common error occurs when placing the exclamation mark directly before the IP address:
iptables -A OUTPUT -d ! 134.134.134.134 -j ACCEPT
This generates the error:
Bad argument 134.134.134.134'
The proper way to use negation in iptables requires placing the exclamation mark immediately after the parameter, before the equals sign:
iptables -A OUTPUT -d ! 134.134.134.134 -j ACCEPT
Alternatively, you can use the long form with equals sign:
iptables -A OUTPUT --destination ! 134.134.134.134 -j ACCEPT
Here's a complete example for redirecting all DNS traffic except to your preferred DNS server:
# Allow traffic to trusted DNS
iptables -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j ACCEPT
# Redirect all other DNS traffic to sinkhole
iptables -A OUTPUT -p udp --dport 53 -d ! 8.8.8.8 -j DNAT --to-destination 192.168.1.100:53
When dealing with CIDR notation or multiple exceptions:
# Exclude multiple IPs using multiple rules
iptables -A OUTPUT -d ! 10.0.0.0/8 -j ACCEPT
iptables -A OUTPUT -d ! 172.16.0.0/12 -j ACCEPT
iptables -A OUTPUT -d ! 192.168.0.0/16 -j ACCEPT
# Combined approach using ipset
ipset create allowed_dns hash:ip
ipset add allowed_dns 8.8.8.8
ipset add allowed_dns 1.1.1.1
iptables -A OUTPUT -p udp --dport 53 -m set ! --match-set allowed_dns dst -j DROP
If you're still encountering issues:
- Ensure there are no spaces between the ! and the IP address
- Verify your iptables version with
iptables --version
- Check for syntax differences between various Linux distributions
- Test with simpler rules first to isolate the problem