Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I need to add a rule to iptables to block connections to a tcp port from the Internet.

Since my script may be called multiple times and there is not a script to delete the rule, I want to check if an iptables rule already exists before inserting it - otherwise there will be a lot of dup rules in the INPUT chain.

How can I check if an iptables rule already exists?

share|improve this question

migrated from stackoverflow.com Nov 22 '11 at 3:14

This question came from our site for professional and enthusiast programmers.

up vote 23 down vote accepted

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"
share|improve this answer

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.

share|improve this answer

Just list and search for it?

iptables --list | grep $ip

... or however you have the rule specified. If you use grep -q it won't output anything, and you can just check the return value with $?

share|improve this answer
3  
I would suggest iptables-save|grep $ip instead as it is a more easily parseable format, especially in a script. You can check the exact syntax of the command too if you want. – Garrett Nov 22 '11 at 4:21
    
Neither of these actually answers the question, because iptables-save|grep $ip could very well match multiple rules. Possibly iptables-save could be used to check for the complete rule specification, but that's still a bit of a hack: the format returned by iptables-save may not match exactly the rule in the script. iptables-save may generate the options in a different order, add things (like -m tcp), and so forth. – larsks Jun 24 '12 at 1:45

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;

share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .