Programmatically managing iptables rules in C: IPTC

Share on Facebook
Share on LinkedIn

Sometimes we need to manage iptables rules from inside our own programs. The recommended solution given by iptables developers is to spawn the iptables command with execl() or system(). It’s explicitly stated that there’s no stable/public API to do that: http://www.netfilter.org/documentation/FAQ/netfilter-faq-4.html#ss4.5

However, if you have control of your box, including kernel, iptables, and all related packages, it’s safe to use IPTC to programmatically manage iptables rules.

IPTC is an internal iptables library that actually inserts/removes the rules. It’s used by the iptables (xtables-multi, iptables-save, iptables-restore) binaries. Depending on how iptables was compiled, there might be a shared libiptc.so object and/or a static libiptc.a object.

You should check that in order to correctly build your application. In Debian/Ubuntu, which this post is based, it’s enough to install the iptables-dev package. It will install the shared libraries, the header files and the pkg-config facility.

So, let’s take a look at a complete function that adds a rule to iptables:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libiptc/libiptc.h>

static int
insert_rule (const char *table,
             const char *chain, 
             unsigned int src,
             int inverted_src,
             unsigned int dest,
             int inverted_dst,
             const char *target)
{
  struct
    {
      struct ipt_entry entry;
      struct xt_standard_target target;
    } entry;
  struct xtc_handle *h;
  int ret = 1;

  h = iptc_init (table);
  if (!h)
    {
      fprintf (stderr, "Could not init IPTC library: %s\n", iptc_strerror (errno));
      goto out;
    }

  memset (&entry, 0, sizeof (entry));

  /* target */
  entry.target.target.u.user.target_size = XT_ALIGN (sizeof (struct xt_standard_target));
  strncpy (entry.target.target.u.user.name, target, sizeof (entry.target.target.u.user.name));

  /* entry */
  entry.entry.target_offset = sizeof (struct ipt_entry);
  entry.entry.next_offset = entry.entry.target_offset + entry.target.target.u.user.target_size;
  
  if (src)
    {
      entry.entry.ip.src.s_addr  = src;
      entry.entry.ip.smsk.s_addr = 0xFFFFFFFF;
      if (inverted_src)
        entry.entry.ip.invflags |= IPT_INV_SRCIP;
    }

  if (dest)
    {
      entry.entry.ip.dst.s_addr  = dest;
      entry.entry.ip.dmsk.s_addr = 0xFFFFFFFF;
      if (inverted_dst)
        entry.entry.ip.invflags |= IPT_INV_DSTIP;
    }

  if (!iptc_append_entry (chain, (struct ipt_entry *) &entry, h))
    {
      fprintf (stderr, "Could not insert a rule in iptables (table %s): %s\n", table, iptc_strerror (errno));
      goto out;
    }

  if (!iptc_commit (h))
    {
      fprintf (stderr, "Could not commit changes in iptables (table %s): %s\n", table, iptc_strerror (errno));
      goto out;
    }

  ret = 0;
out:
  if (h)
    iptc_free (h);

  return ret;
}

int main (int argc, char **argv)
{
  unsigned int a, b;

  inet_pton (AF_INET, "1.2.3.4", &a);
  inet_pton (AF_INET, "4.3.2.1", &b);

  insert_rule ("filter",
               "INPUT",
               a,
               0,
               b,
               1,
               "DROP");
  return 0;
}

Note: The code above was compiled and tested against the 1.4.13 version of iptables.
To compile:

cc `pkg-config –cflags –libs libiptc` source-file.c -o test-iptc

So, the code in main() above does the equivalent of:

iptables -t filter -A INPUT -s 1.2.3.4/32 ! -d 4.3.2.1/32 -j DROP

The main functions used were iptc_init, iptc_append_entry and iptc_commit. There are functions to remove a rule (iptc_delete_num_entry), to iterate over all the rules (iptc_first_rule, iptc_next_rule) and to clear all rules (iptc_flush_entries).

You can check the header file libiptc/libiptc.h to see all available functions and their descriptions. I can post some more examples here in the future if there’s demand to it.

Again, remember that there’s no API stability in IPTC, so, it can change in each new version of iptables.

Hope it’s useful to someone. Feel free to ask anything or to report any issues you have. See you!

1 comment so far ↓

#1 Jorge Pereira on 07.11.12 at 03:57 PM

wow!! it’s a very nice way, there are still people who prefer to use system(), nice job!