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:
insert_rule (const char *table,
const char *chain,
unsigned int src,
unsigned int dest,
const char *target)
struct ipt_entry entry;
struct xt_standard_target target;
struct xtc_handle *h;
int ret = 1;
h = iptc_init (table);
fprintf (stderr, "Could not init IPTC library: %s\n", iptc_strerror (errno));
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;
entry.entry.ip.src.s_addr = src;
entry.entry.ip.smsk.s_addr = 0xFFFFFFFF;
entry.entry.ip.invflags |= IPT_INV_SRCIP;
entry.entry.ip.dst.s_addr = dest;
entry.entry.ip.dmsk.s_addr = 0xFFFFFFFF;
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));
if (!iptc_commit (h))
fprintf (stderr, "Could not commit changes in iptables (table %s): %s\n", table, iptc_strerror (errno));
ret = 0;
int main (int argc, char **argv)
unsigned int a, b;
inet_pton (AF_INET, "184.108.40.206", &a);
inet_pton (AF_INET, "220.127.116.11", &b);
Note: The code above was compiled and tested against the 1.4.13 version of iptables.
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 18.104.22.168/32 ! -d 22.214.171.124/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!