How can I check if an iptables rule already exists?

There is a new -C --check option in recent iptables versions.

# iptables -C INPUT -p tcp --dport 8080 --jump ACCEPT
iptables: Bad rule (does a matching rule exist in that chain?).
# echo $?
1

# iptables -A INPUT -p tcp --dport 8080 --jump ACCEPT

# iptables -C INPUT -p tcp --dport 8080 --jump ACCEPT
# echo $?
0

For older iptables versions, I would use Garrett suggestion :

# iptables-save | grep -- "-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT"

The new -C option is not satisfactory, because it is open to a time-of-check-to-time-of-use (TOCTTOU) race condition. If two processes try to add the same rule at around the same time, -C will not protect them from adding it twice.

So, it is really no better than the grep solution. An accurate text processing job over the output of iptables-save can work as reliably as -C, since that output is a reliable snapshot of the state of the tables.

What is needed is an --ensure option which atomically checks and adds a rule only if it doesn't already exist. Moreover, it would be nice if the rule is moved to the correct position where a new rule would be inserted if it did not exist already (--ensure-move). For instance if iptables -I 1 is used to create a rule at the head of a chain, but that rule exists already in the seventh position, then the existing rule should move to the first position.

Without these features, I think a feasible workaround is to write a shell script loop based on this pseudo code:

while true ; do
  # delete all copies of the rule first

  while copies_of_rule_exist ; do
    iptables -D $RULE
  done

  # now try to add the rule

  iptables -A $RULE # or -I 

  # At this point there may be duplicates due to races.
  # Bail out of loop if there is exactly one, otherwise
  # start again.
  if exactly_one_copy_of_rule_exists ; then
    break;
  fi
done

This code could spin around; it does not guarantee that two or more racers will be out within a fixed number of iterations. Some randomized exponential backoff sleeps could be added to help with that.


This may seem a bit backwards, but it works for me - Try deleting the rule first.

iptables -D INPUT -s xxx.xxx.xxx.xxx -j DROP;

You should get a message similiar to:

iptables: Bad rule (does a matching rule exist in that chain?)

Then simply add your rule as normal:

iptables -A INPUT -s xxx.xxx.xxx.xxx -j DROP;