10

I am trying to programmatically create user chains and delete them in iptables. I was wondering what is the best way to check if a user chain exist and if it does not create it.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
nashr rafeeg
  • 779
  • 3
  • 12
  • 31

3 Answers3

20

Use iptables(8) to list the chain, redirecting stdout/stderr to /dev/null, and check the exit code. If the chain exists, iptables will exit true.

This shell function is from my iptables front-end script:

chain_exists()
{
    [ $# -lt 1 -o $# -gt 2 ] && { 
        echo "Usage: chain_exists <chain_name> [table]" >&2
        return 1
    }
    local chain_name="$1" ; shift
    [ $# -eq 1 ] && local table="--table $1"
    iptables $table -n --list "$chain_name" >/dev/null 2>&1
}

Note that I use the -n option so that iptables does not try to resolve IP addresses to hostnames. Without this, you'll find this function would be slow.

You can then use this function to conditionally create a chain:

chain_exists foo || create_chain foo ...

where create_chain is another function to create the chain. You could call iptables directly, but the above naming makes it quite obvious what is going on.

camh
  • 40,988
  • 13
  • 62
  • 70
  • Should be `iptables(8)` and `iptables -t $table ...`? – mkj Dec 14 '16 at 17:19
  • 1
    @mkj: iptables(8) - I have fixed this, thanks. `-t $table` - look at the line above and you'll see `$table` already contains `--table`. I did it this way so that `$table` is either empty or "--table ", since the table is optional and you don't want -t/--table if there is no table argument.
    – camh Dec 15 '16 at 03:39
  • Using `>/dev/null 2>&1` will lead to hide the error `Could not fetch rule set...` in case a user does not have permission to run `iptables` and is likely to happen if `sudo` is omitted. Adding `sudo` is not a silver bullet because `sudo` may not be available and `>/dev/null 2>&1` will hide the error about its unavailability as well. If intention to use `>/dev/null 2>&1` was to hide `iptables` error when a chain does not exist then you can use `iptables-save` and parse its output without the `>/dev/null 2>&1` like in [this solution](https://stackoverflow.com/a/76799114/1174405) – Jimmix Jul 30 '23 at 20:32
0

For the default table filter and IPv4

Solution:

user_chain='MY_CHAIN'
sudo iptables-save -t filter | awk -F ":| " "/^:${user_chain:?} /"' {rc = 1;}; END { exit !rc }' && printf "Skip create: ${user_chain:?}\n" || \
{ sudo iptables -t filter -N "${user_chain:?}" && printf "Created: ${user_chain:?}\n" ;}

Outputs when run for the first time:

Created: MY_CHAIN

and for the subsequent runs:

Skip create: MY_CHAIN

or if you don't care about prints then solution is:

user_chain='MY_CHAIN'
sudo iptables-save -t filter | awk -F ":| " "/^:${user_chain:?} /"' {rc = 1;}; END { exit !rc }' || \
sudo iptables -t filter -N "${user_chain:?}"

Keep in mind that above examples address only the IPv4, if you want to address IPv6 then change

  • iptables to ip6tables
  • iptables-save to ip6tables-save

in the provided solutions.

Explaination:

  • iptables-save -t filter outputs all rules for the IPv4 filter table including chains names (they are prefixed with :) - remove -t filter for outputting rules and chains names for all tables
  • awk -F ":| " "/^:${user_chain:?} /"' {rc = 1;}; END { exit !rc }' exits with code 0 if user_chain was found and with ec = 1 if not found.
  • || sudo iptables -t filter -N "${user_chain:?}" creates user_chain if awk exited with 1 code (a user_chain was not found)
Jimmix
  • 5,644
  • 6
  • 44
  • 71
0

I was wondering what is the best way to check if a user chain exist and if it does not create it.

If you would accept redefining your requirement to:

I would like to ensure that a user chain exists.

Then quick solution could be:

sudo iptables -N MY_CHAIN || ec=$? && [ ${ec:?} -eq 1 ] || (exit ${ec:?})

The above command ensures that MY_CHAIN exists and exits with 0 code in cases:
iptables exit code - comment

  • 0 - chain MY_CHAIN created
  • 1 - iptables error Chain already exists.

In cases iptables exits with different code than 0 or 1 then command as above exits with that code, for example:

  • 2 - iptables v1.8.7 (nf_tables): chain name MY_CHAIN111111111111111111111111111111111111111' too long (must be under 29 chars)
  • 4 - iptables v1.8.7 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)
Jimmix
  • 5,644
  • 6
  • 44
  • 71