This section explains which functions allow you to query libiptc. We will use
the header file of libiptc,
file usr/local/include/libiptc/libiptc.h,
containing prototypes of each function as a reference to develop our explanation. I have also included a brief description (when available) taken from
Linux netfilter Hacking HOWTO within each function explanation. Name: iptc_init 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. Parameters: tablename is the name of the table we need to query
and/or modify; this could be filter,
mangle, nat, etc. Returns: Pointer to a structure of type iptc_handle_t that must
be used as main parameter for the rest of functions we will call
from libiptc. iptc_init returns the
pointer to the structure or NULL if it fails. If this happens you can invoke
iptc_strerror to get information about the error.
See below. 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)); |
Name: iptc_strerror Usage: Translates error numbers into more human-readable form. Prototype: const char *iptc_strerror(int err) Description: This function returns a more meaningful explanation of a failure code in
the iptc library. If a function fails, it will always set
errno. This value can be passed to
iptc_strerror() to yield an error message. Parameters: err is an integer indicating the error number. Returns: Char pointer containing the error description. Name: iptc_first_chain 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. Parameters: Pointer to a structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Char pointer to the name of the chain. Name: iptc_next_chain 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. Parameters: Pointer to a structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Char pointer to the name of the chain. These two previous functions allow to us to iterate through the chains of the table
getting the name of each of the chains; iptc_first_chain
returns the name of the first chain of the table; iptc_next_chain
returns the name of next chains and NULL when the function reaches the end. 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. Now compile your p1 program: And run it: You will get: 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 |
You will get: INPUT
FORWARD
OUTPUT
chain_1
chain_2 |
Try to generate an error initializing tablename to myfilter
instead of filter. When you compile and execute your program
again, you will get: Error initializing: Table does not exist (do you need to insmod?) |
iptables informs you that myfilter does
not exist as a table. Name: iptc_is_chain Usage: Check if a chain exists. Prototype: int iptc_is_chain(const char *chain, const iptc_handle_t handle) Description: This function checks to see if the chain described in the parameter
chain exists in the table. Parameters: chain is a char pointer containing the name of
the chain we want to check to. handle is a pointer to a
structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: integer value 1 (true) if the chain exists; integer value 0 (false)
if the chain does not exist. Name: iptc_builtin 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. Parameters: chain is a char pointer containing the name of
the chain we want to check to. handle is a pointer to a
structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Returns integer value 1 (true) if the given chain name is the name
of a builtin chain; returns integer value 0 (false) is not. Name: iptc_first_rule Usage: Get first rule in the given chain. Prototype: const struct ipt_entry *iptc_first_rule(const char *chain, iptc_handle_t *handle) Description: This function returns a pointer to the first rule in the given chain
name; NULL for an empty chain. Parameters: chain is a char pointer containing the name of
the chain we want to get the rules to. handle is a
pointer to a structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Returns a pointer to an ipt_entry structure
containing information about the first rule of the chain. See below
for an explanation of this structure. Name: iptc_next_rule Usage: Get the next rule in the given chain. Prototype: const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, iptc_handle_t *handle) Description: This function returns a pointer to the next rule in the given chain
name; NULL means the end of the chain. Parameters: prev is a pointer to a structure of type
ipt_entry that must be obtained first by a previous call to
the function iptc_first_rule. In order to get the second
and subsequent rules you have to pass a pointer to the structure containing the
information about the previous rule of the chain. handle is
a pointer to a structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Returns a pointer to an ipt_entry structure
containing information about the next rule of the chain. See below
for an explanation of this structure. Name: iptc_get_target 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) Description: This function gets the target of the given rule. If it is an extended
target, the name of that target is returned. If it is a jump to another chain,
the name of that chain is returned. If it is a verdict (eg. DROP), that name
is returned. If it has no target (an accounting-style rule), then the empty
string is returned. Note that this function should be used instead of using
the value of the verdict field of the
ipt_entry structure directly, as it offers the above further
interpretations of the standard verdict. Parameters: e is a pointer to a structure of type
ipt_entry that must be obtained first by a previous call to
the function iptc_first_rule or the function
iptc_next_rule. handle is
a pointer to a structure of type iptc_handle_t that was
obtained by a previous call to iptc_init. Returns: Returns a char pointer to the target name. See Description
above for more information. Now it is time to explain the ipt_entry structure; these pieces
of code are taken from iptables package sources: /* 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. Name: iptc_get_policy Usage: Get the policy of a given built-in chain. Prototype: const char *iptc_get_policy(const char *chain, struct ipt_counters *counter, iptc_handle_t *handle) Description: This function gets the policy of a built-in chain, and fills in the
counters argument with the hit statistics on
that policy. Parameters: You have to pass as arguments the name of the built-in chain you want
to get the policy to, a pointer to an ipt_counters
structure to be filled by the function and the
iptc_handle_t structure identifying the table we are
working to. The ipt_counters structure was explained
in previous section; do not forget that iptc_handle_t
must be obtained by a previous call to the function iptc_init. 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] |
Name: iptc_read_counter Usage: Read counters of a rule in a chain. Prototype: struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
unsigned int rulenum, iptc_handle_t *handle); Description: This function read and returns packet and byte counters of the entry
rule in chain chain positioned at
rulenum. Counters are returned in a pointer to a
type structure ipt_counters. Rule numbers start at
1 for the first rule. Parameters: chain is a char pointer to the name of the chain to
be readed; rulenum is an integer value defined the
position in the chain of rules of the rule which counters will be read.
handle is a pointer to a structure of type
iptc_handle_t that was obtained by a previous call to
iptc_init. Returns: Returns a pointer to an ipt_counters structure
containing the byte and packet counters readed. |
|