
I have also included a brief description (when available) taken from Linux netfilter Hacking HOWTO within each function explanation.
Usage: Takes a snapshot of the rules.
Prototype: iptc_handle_t iptc_init(const char *tablename)
Description: This function must be called as initiator before any other function can be called.
Have a look at this section of code in file iptables-save.c for how to invoke this function:
h = iptc_init(tablename);
if (!h)
exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",iptc_strerror(errno));
|
Usage: Translates error numbers into more human-readable form.
Prototype: const char *iptc_strerror(int err)
Usage: Iterator functions to run through the chains.
Prototype: const char *iptc_first_chain(iptc_handle_t *handle)
Description: This function returns the first chain name in the table.
Usage: Iterator functions to run through the chains.
Prototype: const char *iptc_next_chain(iptc_handle_t *handle)
Description: This function returns the next chain name in the table; NULL means no more chains.
Returns: Char pointer to the name of the chain.
We can create Program #1 to exercise our understanding of these previous four functions:
/*
* How to use libiptc- program #1
* /usr/local/src/p1.c
*/
#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"
int main(void)
{
iptc_handle_t h;
const char *chain = NULL;
const char *tablename = "filter";
program_name = "p1";
program_version = NETFILTER_VERSION;
h = iptc_init(tablename);
if ( !h ) {
printf("Error initializing: %s\n", iptc_strerror(errno));
exit(errno);
}
for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h)) {
printf("%s\n", chain);
}
exit(0);
} /* main */
|
Write this program and save it as p1.c in /usr/local/src.
Now write this "bash" script to simplify the compiling process:
#!/bin/bash gcc -Wall -Wunused -DNETFILTER_VERSION=\"1.2.6\" -rdynamic -o $1 $1.c \ /usr/local/lib/iptables.o /usr/local/lib/libiptc.a -ldl |
Save it as ipt-cc and do not forget to chmod 0700 ipt-cc.
bash# ./ipt-cc p1 |
bash# ./p1 |
INPUT FORWARD OUTPUT |
These are the three built-in iptables chains.
Now create some new chains using iptables and run your program again:
bash# iptables -N chain_1 bash# iptables -N chain_2 bash# ./p1 |
INPUT FORWARD OUTPUT chain_1 chain_2 |
Error initializing: Table does not exist (do you need to insmod?) |
iptables informs you that myfilter does not exist as a table.
Usage: Check if a chain exists.
Prototype: int iptc_is_chain(const char *chain, const iptc_handle_t handle)
Usage: Is this a built-in chain?
Prototype: int iptc_builtin(const char *chain, const iptc_handle_t handle)
Description: This function is used to check if a given chain name is a built-in chain or not.
Usage: Get first rule in the given chain.
Prototype: const struct ipt_entry *iptc_first_rule(const char *chain, iptc_handle_t *handle)
Usage: Get a pointer to the target name of this entry.
Prototype: const char *iptc_get_target(const struct ipt_entry *e, iptc_handle_t *handle)
Returns: Returns a char pointer to the target name. See Description above for more information.
/* Internet address. */
struct in_addr {
__u32 s_addr;
};
/* Yes, Virginia, you have to zero the padding. */
struct ipt_ip {
/* Source and destination IP addr */
struct in_addr src, dst;
/* Mask for src and dest IP addr */
struct in_addr smsk, dmsk;
char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
/* Protocol, 0 = ANY */
u_int16_t proto;
/* Flags word */
u_int8_t flags;
/* Inverse flags */
u_int8_t invflags;
};
struct ipt_counters
{
u_int64_t pcnt, bcnt; /* Packet and byte counters */
};
/* This structure defines each of the firewall rules. Consists of 3
parts which are 1) general IP header stuff 2) match specific
stuff 3) the target to perform if the rule matches */
struct ipt_entry
{
struct ipt_ip ip;
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
u_int16_t target_offset;
/* Size of ipt_entry + matches + target */
u_int16_t next_offset;
/* Back pointer */
unsigned int comefrom;
/* Packet and byte counters. */
struct ipt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
|
An ipt_entry structure contains:
An ipt_ip structure containing (for the rule) the source address and netmask (ip.src.s_addr, ip.smsk.s_addr), the destination address and netmask (ip.dst.s_addr, ip.dmsk.s_addr), the protocol (ip.proto), a flags field (invflags) to check for inverse (!) selections (eg. ! 192.168.2.0/24, ! eth0, ! tcp, etc), the input interface (iniface), the output interface (outiface), the input (iniface_mask) and output (outiface_mask) interface masks and the flags field to check for fragmented packets.
An ipt_counters structure containing the packet (pcnt) and byte (bcnt) counter of the rule. This information is important for bandwidth measurement.
target_offset that is used to get the target information of the rule.
Unknown fields: nfcache, comefrom, elems, next_offset. If someone can give me a feedback about these fields I would be grateful.
A simple way to work with all this information is to borrow some functions from iptables-save.c by Paul Russell and Harald Welte.
Here is another sample program Program #2 written with a lot of help from Russell-Welte:
/*
* How to use libiptc- program #2
* /usr/local/src/p1.c
*/
#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"
/* Here begins some of the code taken from iptables-save.c **************** */
#define IP_PARTS_NATIVE(n) \
(unsigned int)((n)>>24)&0xFF, \
(unsigned int)((n)>>16)&0xFF, \
(unsigned int)((n)>>8)&0xFF, \
(unsigned int)((n)&0xFF)
#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
/* This assumes that mask is contiguous, and byte-bounded. */
static void
print_iface(char letter, const char *iface, const unsigned char *mask,
int invert)
{
unsigned int i;
if (mask[0] == 0)
return;
printf("-%c %s", letter, invert ? "! " : "");
for (i = 0; i < IFNAMSIZ; i++) {
if (mask[i] != 0) {
if (iface[i] != '\0')
printf("%c", iface[i]);
} else {
/* we can access iface[i-1] here, because
* a few lines above we make sure that mask[0] != 0 */
if (iface[i-1] != '\0')
printf("+");
break;
}
}
printf(" ");
}
/* These are hardcoded backups in iptables.c, so they are safe */
struct pprot {
char *name;
u_int8_t num;
};
/* FIXME: why don't we use /etc/protocols ? */
static const struct pprot chain_protos[] = {
{ "tcp", IPPROTO_TCP },
{ "udp", IPPROTO_UDP },
{ "icmp", IPPROTO_ICMP },
{ "esp", IPPROTO_ESP },
{ "ah", IPPROTO_AH },
};
static void print_proto(u_int16_t proto, int invert)
{
if (proto) {
unsigned int i;
const char *invertstr = invert ? "! " : "";
for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
if (chain_protos[i].num == proto) {
printf("-p %s%s ",
invertstr, chain_protos[i].name);
return;
}
printf("-p %s%u ", invertstr, proto);
}
}
static int print_match(const struct ipt_entry_match *e,
const struct ipt_ip *ip)
{
struct iptables_match *match
= find_match(e->u.user.name, TRY_LOAD);
if (match) {
printf("-m %s ", e->u.user.name);
/* some matches don't provide a save function */
if (match->save)
match->save(ip, e);
} else {
if (e->u.match_size) {
fprintf(stderr,
"Can't find library for match `%s'\n",
e->u.user.name);
exit(1);
}
}
return 0;
}
/* print a given ip including mask if neccessary */
static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
{
if (!mask && !ip)
return;
printf("%s %s%u.%u.%u.%u",
prefix,
invert ? "! " : "",
IP_PARTS(ip));
if (mask != 0xffffffff)
printf("/%u.%u.%u.%u ", IP_PARTS(mask));
else
printf(" ");
}
/* We want this to be readable, so only print out neccessary fields.
* Because that's the kind of world I want to live in. */
static void print_rule(const struct ipt_entry *e,
iptc_handle_t *h, const char *chain, int counters)
{
struct ipt_entry_target *t;
const char *target_name;
/* print counters */
if (counters)
printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
/* print chain name */
printf("-A %s ", chain);
/* Print IP part. */
print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
e->ip.invflags & IPT_INV_SRCIP);
print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
e->ip.invflags & IPT_INV_DSTIP);
print_iface('i', e->ip.iniface, e->ip.iniface_mask,
e->ip.invflags & IPT_INV_VIA_IN);
print_iface('o', e->ip.outiface, e->ip.outiface_mask,
e->ip.invflags & IPT_INV_VIA_OUT);
print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
if (e->ip.flags & IPT_F_FRAG)
printf("%s-f ",
e->ip.invflags & IPT_INV_FRAG ? "! " : "");
/* Print matchinfo part */
if (e->target_offset) {
IPT_MATCH_ITERATE(e, print_match, &e->ip);
}
/* Print target name */
target_name = iptc_get_target(e, h);
if (target_name && (*target_name != '\0'))
printf("-j %s ", target_name);
/* Print targinfo part */
t = ipt_get_target((struct ipt_entry *)e);
if (t->u.user.name[0]) {
struct iptables_target *target
= find_target(t->u.user.name, TRY_LOAD);
if (!target) {
fprintf(stderr, "Can't find library for target `%s'\n",
t->u.user.name);
exit(1);
}
if (target->save)
target->save(&e->ip, t);
else {
/* If the target size is greater than ipt_entry_target
* there is something to be saved, we just don't know
* how to print it */
if (t->u.target_size !=
sizeof(struct ipt_entry_target)) {
fprintf(stderr, "Target `%s' is missing "
"save function\n",
t->u.user.name);
exit(1);
}
}
}
printf("\n");
}
/* Here ends some of the code taken from iptables-save.c ****************** */
int main(void)
{
iptc_handle_t h;
const struct ipt_entry *e;
const char *chain = NULL;
const char *tablename = "filter";
const int counters = 1;
program_name = "p2";
program_version = NETFILTER_VERSION;
/* initialize */
h = iptc_init(tablename);
if ( !h ) {
printf("Error initializing: %s\n", iptc_strerror(errno));
exit(errno);
}
/* print chains and their rules */
for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h)) {
printf("%s\n", chain);
for (e = iptc_first_rule(chain, &h); e; e = iptc_next_rule(e, &h)) {
print_rule(e, &h, chain, counters);
}
}
exit(0);
} /* main */
|
The function print_rule borrowed from iptables-save.c prints the information about a rule into a readable form using:
print_ip to print the addresses,
print_iface to print the interfaces,
print_proto to print the protocols,
iptc_get_target to get and print the targets (using save).
In main we iterate through each chain and for each one we iterate through each rule printing it.
The arguments of print_rule are:
e = pointer to an ipt_entry structure containing information about the rule.
h = pointer to an iptc_handle_t structure returned by iptc_init.
chain = name of the chain.
counters = 0: do not print counters; 1: print them.
OK, compile and run program p2:
bash# ./ipt-cc p2 bash# ./p2 |
You will get:
INPUT FORWARD OUTPUT chain_1 chain_2 |
Now modify the environment using iptables to add some rules:
bash# iptables -A INPUT -p tcp -i eth0 -s ! 192.168.1.1 --dport 20 -j ACCEPT bash# iptables -A chain_1 -p udp -o eth1 -s 192.168.2.0/24 --sport 33 -j DROP |
Now if you run again p2 you will get:
INPUT [0:0] -A INPUT -s ! 192.168.1.1 -i eth0 -p tcp -m tcp --dport 20 -j ACCEPT FORWARD OUTPUT chain_1 [0:0] -A chain_1 -s 192.168.2.0/255.255.255.0 -o eth1 -p udp -m udp --sport 33 -j DROP chain_2 |
We have now rules printed for INPUT and chain_1 chains. The numbers in the brackets at left are packet and byte counters respectively.
Usage: Get the policy of a given built-in chain.
Returns: Returns a char pointer to the policy name.
Using pieces of programs 1 and 2 we can write program #3:
/*
* How to use libiptc- program #3
* /usr/local/src/p3.c
*/
#include <getopt.h>
#include <sys/errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include "libiptc/libiptc.h"
#include "iptables.h"
int main(void)
{
iptc_handle_t h;
const char *chain = NULL;
const char *policy = NULL;
const char *tablename = "filter";
struct ipt_counters counters;
program_name = "p3";
program_version = NETFILTER_VERSION;
/* initialize */
h = iptc_init(tablename);
if ( !h ) {
printf("Error initializing: %s\n", iptc_strerror(errno));
exit(errno);
}
/* print built-in chains, their policies and counters */
printf("BUILT-IN POLICY PKTS-BYTES\n");
printf("-----------------------------\n");
for (chain = iptc_first_chain(&h); chain; chain = iptc_next_chain(&h)) {
if ( !iptc_builtin(chain, h) )
continue;
if ( (policy = iptc_get_policy(chain, &counters, &h)) )
printf("%-10s %-10s [%llu:%llu]\n",
chain, policy, counters.pcnt, counters.bcnt);
}
exit(0);
} /* main */
|
OK, compile and run program p3:
bash# ./ipt-cc p3 bash# ./p3 |
You will get something like this:
BUILT-IN POLICY PKTS-BYTES ---------------------------- INPUT ACCEPT [0:0] FORWARD ACCEPT [0:0] OUTPUT ACCEPT [0:0] |