LDP/LDP/howto/docbook/Querying-libiptc-HOWTO.sgml

3015 lines
91 KiB
Plaintext

<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
<article id="qlibiptc" lang="en">
<articleinfo>
<title>Querying libiptc HOWTO</title>
<author>
<firstname>Leonardo</firstname>
<surname>Balliache</surname>
<affiliation>
<address>
<email>
leonardo@opalsoft.net
</email>
</address>
</affiliation>
</author>
<pubdate>Version 0.1 - April 30, 2002</pubdate>
<revhistory>
<revision>
<revnumber>0.1</revnumber>
<date>2002-04-30</date>
<authorinitials>lb</authorinitials>
<revremark>
Initial release.
</revremark>
</revision>
</revhistory>
</articleinfo>
<sect1 id="legal" label="1">
<title>Legal Notice</title>
<para>
This document is free; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version. This document is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details. You can get a copy of the GNU GPL
<ulink url="http://www.gnu.org/copyleft/gpl.html">here</ulink>.
</para>
</sect1>
<sect1 id="translat" label="2">
<title>Translations</title>
<para>
If you want to translate this document you are free to do so. However,
you will need to do the following:
</para>
<orderedlist>
<listitem>
<para>
Check first that another version of the document doesn't already
exist at your local LDP.
</para>
</listitem>
<listitem>
<para>
Maintain all sections (including 'Legal Notice', 'Translation',
'Credits', etc., etc.) of the document.
</para>
</listitem>
<listitem>
<para>
No need to ask me to translate! You just have to let me know
(if you want) about your translation.
</para>
</listitem>
</orderedlist>
<para>
Thank for your translation.
</para>
</sect1>
<sect1 id="disclaim" label="3">
<title>Disclaimer</title>
<para>
I took this <quote>disclaimer</quote> from
<ulink url="http://qos.ittc.ukans.edu/howto/howto.html">
Linux-Advance Networking Overview</ulink> by
Saravanan Radhakrishnan (08-1999) because it applies in my own case:
</para>
<para>
All the text in this document is completely based on my understanding
of implementations of various features. I have read some documents and
have seen the code myself, and I described them based on my understanding.
If the reader notices any concept description which appears to be contrary
to their understanding of the concept, the issue can be taken up for
discussion and corrections will be made to the document as necessary. I
would appreciate any suggestions and comments made in order to improve the
quality of this document.
</para>
</sect1>
<sect1 id="credits" label="4">
<title>Credits</title>
<para>
I want to thank the following people and organizations who had helped me,
directly or not, to make this document possible:
</para>
<itemizedlist>
<listitem>
<para>
My wife Cielo and my sons Jose, Dario and Gustavo by their patient and
support.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.tldp.org">Linux Documentation Project</ulink>
for publishing and uploading my document.
</para>
</listitem>
<listitem>
<para>
The site <ulink url="http://www.docum.org">http://www.docum.org</ulink>
drived by Stef Coene that give me some ideas for writing this document.
</para>
</listitem>
<listitem>
<para>
Paul <quote>Rusty</quote> Rusell who write the kernel firewall code,
the excelent package <emphasis>iptables</emphasis> and the
associated library <emphasis>libiptc</emphasis>.
</para>
</listitem>
<listitem>
<para>
Harald Welte who write the utility <emphasis>iptables-save</emphasis>.
</para>
</listitem>
<listitem>
<para>
Alexey Kuznetsov who write the kernel queue discipline code and the
excelent package <emphasis>iproute2</emphasis>.
</para>
</listitem>
<listitem>
<para>
Tabatha Persad from the
<ulink url="http://www.tldp.org">Linux Documentation Project</ulink>
who revised my english syntax and writing, gave me several ideas to
improve the content and encouraged me to learn and use DocBook to write
the final version of this document.
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="object" label="5">
<title>Objectives</title>
<para>
This HOWTO explains how to use the <emphasis>libiptc</emphasis> library
included in the <emphasis>iptables</emphasis> package. This document can
show you how to use short C or C++ programs to query the internal structure
of the firewalling code, to check chains and rules, packet and byte counters,
and in a second phase, if you are a little <quote>brave</quote>, to modify
them.
</para>
<para>
You can find the latest version of this document at
<ulink url="http://opalsoft.net/qos/libiptc/qlibiptc.html">
Querying libiptc HOWTO.html</ulink>.
</para>
<para>
If you have suggestions to help make this document better, please submit
your ideas to me at the following address:
<ulink url="mailto:leonardo@opalsoft.net">leonardo@opalsoft.net</ulink>.
</para>
<para>
While I wrote this HOWTO, I developed a simple bandwith meter using
user-defined chains to get the data to be measured. This idea was conceived
looking at <command>monitor.pl</command>, a simple perl program for bandwith
measurement, written by Stef Coene at
<ulink url="http://www.docum.org">http://www.docum.org</ulink>.
I recommend this site to people interested in bandwidth control and
measurement.
</para>
</sect1>
<sect1 id="whatis"label="6">
<title>What is libiptc?</title>
<para>
<emphasis>libiptc</emphasis> is the library that is used to communicate
with netfilter, the internal kernel code in charge of firewalling and packet
filtering. This code and <emphasis>iptables</emphasis> were written by Paul
<quote>Rusty</quote> Russell. <emphasis>iptables</emphasis> was developed
using <emphasis>libiptc</emphasis> calls to get the job done.
</para>
<para>
If you want to have more information about <emphasis>iptables</emphasis>,
<emphasis>libiptc</emphasis> and the firewalling code, have a look at
links at the end of this document.
</para>
</sect1>
<sect1 id="howdidi" label="7">
<title>How did I obtain this knowledge?</title>
<para>
Just looking at code in <emphasis>iptables 1.2.6</emphasis> package and
especially at program <emphasis>iptables-save.c</emphasis> that use
<emphasis>libiptc</emphasis> to dump information from firewalling
kernel code.
</para>
<para>
I will try to be very pragmatic and clear in order to make this HOWTO
useful.
</para>
</sect1>
<sect1 id="previous" label="8">
<title>Previous knowledge and system requirements</title>
<para>
You have to have some previous knowledge to follow this document:
</para>
<orderedlist>
<listitem>
<para>
<emphasis>Very important:</emphasis>
You must know how to use the <emphasis>iptables</emphasis> package as a
user, such as how to create or list rules and user chains. You do not need
to be a firewall expert, but you should know how to use
<emphasis>iptables</emphasis> fluently.
</para>
</listitem>
<listitem>
<para>
You have to have kernel sources installed in your system, in
<filename>/usr/src/linux</filename> as usual.
</para>
<para>
I am using a <emphasis>2.4.16</emphasis> kernel in a
<emphasis>SuSE 7.1</emphasis> Linux environment. You need
<emphasis>2.4.x</emphasis> kernel code to follow this HOWTO, preferably kernel
<emphasis>2.4.16</emphasis>. For <emphasis>SuSE</emphasis> you can
get the kernel sources at
<ulink url="ftp://ftp.gwdg.de/pub/linux/suse/ftp.suse.com/suse/i386/update/">
ftp://ftp.gwdg.de/pub/linux/suse/ftp.suse.com/suse/i386/update</ulink>.
</para>
</listitem>
<listitem>
<para>
You have to know how to compile the kernel if you have to update your kernel
version. After activating the netfilter options using
<command>make menuconfig</command>, you
must compile and install the kernel as usual.
</para>
</listitem>
<listitem>
<para>
Reboot your new kernel using <command>init 6</command>. Ensure that you
backup a copy of your previous kernel in <emphasis>lilo</emphasis> in case
you encounter a problem and need to retrace your steps.
</para>
</listitem>
<listitem>
<para>
Be sure that your new <emphasis>2.4.x</emphasis> kernel is running fine.
To install <emphasis>iptables-1.2.6</emphasis> you will need to patch the
kernel again (and re-compile and install it), and it is better if you follow
the previous two steps to ensure that your kernel is running right before
applying new iptables patches.
</para>
</listitem>
</orderedlist>
</sect1>
<sect1 id="install" label="9">
<title>Installing iptables + libiptc</title>
<para>
To install <emphasis>libiptc</emphasis> follow these steps:
</para>
<orderedlist>
<listitem>
<para>
Download <emphasis>iptables-1.2.6.tar.bz2</emphasis> from
<ulink url="http://netfilter.samba.org/">http://netfilter.samba.org/</ulink>.
</para>
</listitem>
<listitem>
<para>
Copy the <emphasis>iptables</emphasis> tar file into
<filename>/usr/local/src</filename>:
</para>
<screen>
bash# <command>cp iptables-1.2.6.tar.bz2 /usr/local/src</command>
</screen>
</listitem>
<listitem>
<para> Unpack:</para>
<screen>
bash# <command>tar xjvf iptables-1.2.6.tar.bz2</command>
</screen>
</listitem>
<listitem>
<para> Go into the iptables directory:</para>
<screen>
bash# <command>cd iptables-1.2.6</command>
</screen>
</listitem>
<listitem>
<para>
Check to see if your kernel needs some aditional patches with:
</para>
<screen>
bash# <command>make pending-patches</command> <emphasis>KERNEL_DIR=/usr/src/linux</emphasis>
</screen>
<para>
If your kernel source is located somewhere other than in
<filename>/usr/src/linux</filename>, replace the kernel source directory in
the command line above with your source directory.
</para>
<para>
Be careful with this option. This command invokes
<emphasis>patch-o-matic</emphasis>, a new patch verification utility by
Rusty Russell. The utility will show you a list of new patches (some
proposed, some submitted, some accepted) available for your kernel source.
As Rusty himself says, <quote>Some of these new patches have bugs</quote>,
and you do not have to apply all of them.
</para>
<para>
Read the information showed for each patch carefully and answer with
<keycap>y</keycap> (apply the patch) or <keycap>N</keycap> (skip this patch).
In some cases answering <keycap>y</keycap> will try to apply the patch, but
if the patch finds some differences between your sources, it will be
skipped and the next new one presented.
</para>
<para>
I did not apply any of the proposed patches and kept my kernel in its
original state before continuing to the next step.
</para>
</listitem>
<listitem>
<para>
Make the iptables package with:
</para>
<screen>
bash# <command>make</command> <emphasis>KERNEL_DIR=/usr/src/linux</emphasis>
</screen>
<para>
Again, if your kernel source is not at <filename>/usr/src/linux</filename>,
replace the kernel source directory in the command above.
</para>
<para>
If all goes right the compiler will finish without errors.
</para>
</listitem>
<listitem>
<para>
Before the next step, check to see if you have installed iptables package by
typing:
</para>
<screen>
bash# <command>rpm -q iptables</command>
</screen>
<para>
If the iptables rpm is installed, you will see the name and version of the
package, similar to:
</para>
<para>
<emphasis>iptables-1.1.2-13</emphasis>
</para>
<para>
In this case un-install with:
</para>
<screen>
bash# <command>rpm -e iptables</command>
</screen>
</listitem>
<listitem>
<para>
Install the new created package:
</para>
<screen>
bash# <command>make install</command> <emphasis>KERNEL_DIR=/usr/src/linux</emphasis>
</screen>
<para>
Again, check your kernel source directory.
</para>
<para>
This command will install the binaries (<emphasis>iptables, iptables-save,
iptables-restore</emphasis>) in <filename>/usr/local/sbin</filename>, the manuals
in <filename>/usr/local/man/man8</filename> and the modules in
<filename>/usr/local/lib/iptables</filename>.
</para>
</listitem>
<listitem>
<para>
Finally install the headers, development libraries and associated
development man pages, with:
</para>
<screen>
bash# <command>make install-devel</command>
</screen>
<para>
This command will install the <emphasis>libiptc</emphasis> library
in <filename>/usr/local/lib</filename>.
</para>
<para>
I think something must be wrong with this command. It does not install all
headers files properly, so you must install them yourself using:
</para>
<screen>
bash# <command>cd /usr/local/src/iptables-1.2.6</command>
bash# <command>cp include/iptables.h /usr/local/include</command>
bash# <command>cp include/iptables_common.h /usr/local/include</command>
bash# <command>mkdir /usr/local/include/libiptc</command>
bash# <command>cp include/libiptc/libiptc.h /usr/local/include/libiptc</command>
bash# <command>cp include/libiptc/ipt_kernel_headers.h /usr/local/include/libiptc</command>
bash# <command>cp iptables.o /usr/local/lib</command>
</screen>
<para>
<filename>iptables.o</filename> is needed above to compile programs to get
rule information from netfilter.
</para>
<para>
Now you are ready to create programs that can communicate directly with libiptc.
</para>
</listitem>
</orderedlist>
</sect1>
<sect1 id="howtoprg" label="10">
<title>How to create your program(s)</title>
<para>
Create your program(s) in <filename>/usr/local/src</filename>;
this way you will not have problems with gcc looking for files in the "include" section.
</para>
<para>
Your program(s) would be something like this:
</para>
<screen>
/* My program */
#include &#60;getopt.h&#62;
#include &#60;sys/errno.h&#62;
#include &#60;stdio.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;dlfcn.h&#62;
#include &#60;time.h&#62;
#include "libiptc/libiptc.h"
#include "iptables.h"
int main(void)
{
<emphasis>
/* Use always this part for your programs .... From here ... **** */
</emphasis>
iptc_handle_t h;
const char *chain = NULL;
const char *tablename = NULL;
program_name = "my_program";
program_version = NETFILTER_VERSION;
<emphasis>
/* .... To here .... ******************************************** */
</emphasis>
<emphasis>
/* From here you write your own code */
</emphasis>
.... your code ...
....
....
} /* main */
</screen>
<itemizedlist>
<listitem>
<para>
The "include" section is <emphasis>a must</emphasis> in your c/c++ program(s).
</para>
</listitem>
<listitem>
<para>
If you are using c++ do not forget to write extern "C" for
these include.
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="qfunction" label="11">
<title>Functions to query libiptc</title>
<para>
This section explains which functions allow you to query libiptc. We will use
the header file of <emphasis>libiptc</emphasis>,
file <filename>usr/local/include/libiptc/libiptc.h</filename>,
containing prototypes of each function as a reference to develop our explanation.
</para>
<para>
I have also included a brief description (when available) taken from
<ulink url="http://netfilter.samba.org/documentation/HOWTO/">
Linux netfilter Hacking HOWTO</ulink> within each function explanation.
</para>
<sect2 label="11.1">
<title>iptc_init</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_init
</seg>
<seg>
Takes a snapshot of the rules.
</seg>
<seg>
iptc_handle_t iptc_init(const char *tablename)
</seg>
<seg>
This function must be called as initiator before any other function can be
called.
</seg>
<seg>
<emphasis>tablename</emphasis> is the name of the table we need to query
and/or modify; this could be <emphasis>filter</emphasis>,
<emphasis>mangle</emphasis>, <emphasis>nat</emphasis>, etc.
</seg>
<seg>
Pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that must
be used as main parameter for the rest of functions we will call
from <emphasis>libiptc</emphasis>. <emphasis>iptc_init</emphasis> returns the
pointer to the structure or NULL if it fails. If this happens you can invoke
<emphasis>iptc_strerror</emphasis> to get information about the error.
See below.
</seg>
</seglistitem>
</segmentedlist>
<para>
Have a look at this section of code in file <filename>iptables-save.c</filename>
for how to invoke this function:
</para>
<screen>
h = iptc_init(tablename);
if (!h)
exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",iptc_strerror(errno));
</screen>
</sect2>
<sect2 label="11.2">
<title>iptc_strerror</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_strerror
</seg>
<seg>
Translates error numbers into more human-readable form.
</seg>
<seg>
const char *iptc_strerror(int err)
</seg>
<seg>
This function returns a more meaningful explanation of a failure code in
the iptc library. If a function fails, it will always set
<emphasis>errno</emphasis>. This value can be passed to
<emphasis>iptc_strerror()</emphasis> to yield an error message.
</seg>
<seg>
<emphasis>err</emphasis> is an integer indicating the error number.
</seg>
<seg>
Char pointer containing the error description.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.3">
<title>iptc_first_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_first_chain
</seg>
<seg>
Iterator functions to run through the chains.
</seg>
<seg>
const char *iptc_first_chain(iptc_handle_t *handle)
</seg>
<seg>
This function returns the first chain name in the table.
</seg>
<seg>
Pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Char pointer to the name of the chain.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.4">
<title>iptc_next_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_next_chain
</seg>
<seg>
Iterator functions to run through the chains.
</seg>
<seg>
const char *iptc_next_chain(iptc_handle_t *handle)
</seg>
<seg>
This function returns the next chain name in the table; NULL means
no more chains.
</seg>
<seg>
Pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Char pointer to the name of the chain.
</seg>
</seglistitem>
</segmentedlist>
<para>
These two previous functions allow to us to iterate through the chains of the table
getting the name of each of the chains; <emphasis>iptc_first_chain</emphasis>
returns the name of the first chain of the table; <emphasis>iptc_next_chain</emphasis>
returns the name of next chains and NULL when the function reaches the end.
</para>
<para>
We can create <emphasis>Program #1</emphasis> to exercise our understanding of
these previous four functions:
</para>
<screen>
/*
* How to use libiptc- program #1
* /usr/local/src/p1.c
*/
#include &#60;getopt.h&#62;
#include &#60;sys/errno.h&#62;
#include &#60;stdio.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;dlfcn.h&#62;
#include &#60;time.h&#62;
#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(&#38;h); chain; chain = iptc_next_chain(&#38;h)) {
printf("%s\n", chain);
}
exit(0);
} /* main */
</screen>
<para>
Write this program and save it as <filename>p1.c</filename>
in <filename>/usr/local/src</filename>.
</para>
<para>
Now write this <quote>bash</quote> script to simplify the compiling process:
</para>
<screen>
#!/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
</screen>
<para>
Save it as <filename>ipt-cc</filename> and do not forget to
<emphasis>chmod 0700 ipt-cc</emphasis>.
</para>
<para>
Now compile your <emphasis>p1</emphasis> program:
</para>
<screen>
bash# <command>./ipt-cc p1</command>
</screen>
<para>
And run it:
</para>
<screen>
bash# <command>./p1</command>
</screen>
<para>
You will get:
</para>
<screen>
INPUT
FORWARD
OUTPUT
</screen>
<para>
These are the three built-in iptables chains.
</para>
<para>
Now create some new chains using iptables and run your program again:
</para>
<screen>
bash# <command>iptables -N chain_1</command>
bash# <command>iptables -N chain_2</command>
bash# <command>./p1</command>
</screen>
<para>
You will get:
</para>
<screen>
INPUT
FORWARD
OUTPUT
chain_1
chain_2
</screen>
<para>
Try to generate an error initializing tablename to <emphasis>myfilter</emphasis>
instead of <emphasis>filter</emphasis>. When you compile and execute your program
again, you will get:
</para>
<screen>
<emphasis>Error initializing: Table does not exist (do you need to insmod?)</emphasis>
</screen>
<para>
<emphasis>iptables</emphasis> informs you that <emphasis>myfilter</emphasis> does
not exist as a table.
</para>
</sect2>
<sect2 label="11.5">
<title>iptc_is_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_is_chain
</seg>
<seg>
Check if a chain exists.
</seg>
<seg>
int iptc_is_chain(const char *chain, const iptc_handle_t handle)
</seg>
<seg>
This function checks to see if the chain described in the parameter
<emphasis>chain</emphasis> exists in the table.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer containing the name of
the chain we want to check to. <emphasis>handle</emphasis> is a pointer to a
structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
integer value 1 (true) if the chain exists; integer value 0 (false)
if the chain does not exist.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.6">
<title>iptc_builtin</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_builtin
</seg>
<seg>
Is this a built-in chain?
</seg>
<seg>
int iptc_builtin(const char *chain, const iptc_handle_t handle)
</seg>
<seg>
This function is used to check if a given chain name is a built-in
chain or not.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer containing the name of
the chain we want to check to. <emphasis>handle</emphasis> is a pointer to a
structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if the given chain name is the name
of a builtin chain; returns integer value 0 (false) is not.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.7">
<title>iptc_first_rule</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_first_rule
</seg>
<seg>
Get first rule in the given chain.
</seg>
<seg>
const struct ipt_entry *iptc_first_rule(const char *chain, iptc_handle_t *handle)
</seg>
<seg>
This function returns a pointer to the first rule in the given chain
name; NULL for an empty chain.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer containing the name of
the chain we want to get the rules to. <emphasis>handle</emphasis> is a
pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns a pointer to an <emphasis>ipt_entry</emphasis> structure
containing information about the first rule of the chain. See below
for an explanation of this structure.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.8">
<title>iptc_next_rule</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_next_rule
</seg>
<seg>
Get the next rule in the given chain.
</seg>
<seg>
const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, iptc_handle_t *handle)
</seg>
<seg>
This function returns a pointer to the next rule in the given chain
name; NULL means the end of the chain.
</seg>
<seg>
<emphasis>prev</emphasis> is a pointer to a structure of type
<emphasis>ipt_entry</emphasis> that must be obtained first by a previous call to
the function <emphasis>iptc_first_rule</emphasis>. 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. <emphasis>handle</emphasis> is
a pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns a pointer to an <emphasis>ipt_entry</emphasis> structure
containing information about the next rule of the chain. See below
for an explanation of this structure.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="11.9">
<title>iptc_get_target</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_get_target
</seg>
<seg>
Get a pointer to the target name of this entry.
</seg>
<seg>
const char *iptc_get_target(const struct ipt_entry *e, iptc_handle_t *handle)
</seg>
<seg>
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 <emphasis>verdict</emphasis> field of the
<emphasis>ipt_entry</emphasis> structure directly, as it offers the above further
interpretations of the standard verdict.
</seg>
<seg>
<emphasis>e</emphasis> is a pointer to a structure of type
<emphasis>ipt_entry</emphasis> that must be obtained first by a previous call to
the function <emphasis>iptc_first_rule</emphasis> or the function
<emphasis>iptc_next_rule</emphasis>. <emphasis>handle</emphasis> is
a pointer to a structure of type <emphasis>iptc_handle_t</emphasis> that was
obtained by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns a char pointer to the target name. See <emphasis>Description</emphasis>
above for more information.
</seg>
</seglistitem>
</segmentedlist>
<para>
Now it is time to explain the <emphasis>ipt_entry</emphasis> structure; these pieces
of code are taken from <emphasis>iptables</emphasis> package sources:
</para>
<screen>
/* 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];
};
</screen>
<para>
An <emphasis>ipt_entry</emphasis> structure contains:
</para>
<itemizedlist>
<listitem>
<para>
An <emphasis>ipt_ip</emphasis> structure containing (for the rule) the
source address and netmask <emphasis>(ip.src.s_addr, ip.smsk.s_addr)</emphasis>,
the destination address and netmask <emphasis>(ip.dst.s_addr, ip.dmsk.s_addr)</emphasis>,
the protocol <emphasis>(ip.proto)</emphasis>, a flags field <emphasis>(invflags)</emphasis>
to check for inverse <emphasis>(!)</emphasis> selections
<emphasis>(eg. ! 192.168.2.0/24, ! eth0, ! tcp, etc)</emphasis>, the input
interface <emphasis>(iniface)</emphasis>, the output interface
<emphasis>(outiface)</emphasis>, the input <emphasis>(iniface_mask)</emphasis>
and output <emphasis>(outiface_mask)</emphasis> interface masks and the
<emphasis>flags</emphasis> field to check for fragmented packets.
</para>
</listitem>
<listitem>
<para>
An <emphasis>ipt_counters</emphasis> structure containing the packet
<emphasis>(pcnt)</emphasis> and byte <emphasis>(bcnt)</emphasis> counter
of the rule. This information is important for bandwidth measurement.
</para>
</listitem>
<listitem>
<para>
<emphasis>target_offset</emphasis> that is used to get the target information
of the rule.
</para>
</listitem>
<listitem>
<para>
Unknown fields: <emphasis>nfcache, comefrom, elems, next_offset</emphasis>.
If someone can give me a feedback about these fields I would be grateful.
</para>
</listitem>
</itemizedlist>
<para>
A simple way to work with all this information is to borrow
some functions from <filename>iptables-save.c</filename> by Paul
Russell and Harald Welte.
</para>
<para>
Here is another sample program <emphasis>Program #2</emphasis> written with
a lot of help from Russell-Welte:
</para>
<screen>
/*
* How to use libiptc- program #2
* /usr/local/src/p1.c
*/
#include &#60;getopt.h&#62;
#include &#60;sys/errno.h&#62;
#include &#60;stdio.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;dlfcn.h&#62;
#include &#60;time.h&#62;
#include "libiptc/libiptc.h"
#include "iptables.h"
<emphasis>
/* Here begins some of the code taken from iptables-save.c **************** */
</emphasis>
#define IP_PARTS_NATIVE(n) \
(unsigned int)((n)>>24)&#38;0xFF, \
(unsigned int)((n)>>16)&#38;0xFF, \
(unsigned int)((n)>>8)&#38;0xFF, \
(unsigned int)((n)&#38;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 &#60; 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 &#60; 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 &#38;&#38; !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 &#38; IPT_INV_SRCIP);
print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
e->ip.invflags &#38; IPT_INV_DSTIP);
print_iface('i', e->ip.iniface, e->ip.iniface_mask,
e->ip.invflags &#38; IPT_INV_VIA_IN);
print_iface('o', e->ip.outiface, e->ip.outiface_mask,
e->ip.invflags &#38; IPT_INV_VIA_OUT);
print_proto(e->ip.proto, e->ip.invflags &#38; IPT_INV_PROTO);
if (e->ip.flags &#38; IPT_F_FRAG)
printf("%s-f ",
e->ip.invflags &#38; IPT_INV_FRAG ? "! " : "");
/* Print matchinfo part */
if (e->target_offset) {
IPT_MATCH_ITERATE(e, print_match, &#38;e->ip);
}
/* Print target name */
target_name = iptc_get_target(e, h);
if (target_name &#38;&#38; (*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(&#38;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");
}
<emphasis>
/* Here ends some of the code taken from iptables-save.c ****************** */
</emphasis>
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(&#38;h); chain; chain = iptc_next_chain(&#38;h)) {
printf("%s\n", chain);
for (e = iptc_first_rule(chain, &#38;h); e; e = iptc_next_rule(e, &#38;h)) {
print_rule(e, &#38;h, chain, counters);
}
}
exit(0);
} /* main */
</screen>
<para>
The function <emphasis>print_rule</emphasis> borrowed from
<filename>iptables-save.c</filename> prints the information
about a rule into a readable form using:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>print_ip</emphasis> to print the addresses,
</para>
</listitem>
<listitem>
<para>
<emphasis>print_iface</emphasis> to print the interfaces,
</para>
</listitem>
<listitem>
<para>
<emphasis>print_proto</emphasis> to print the protocols,
</para>
</listitem>
<listitem>
<para>
<emphasis>iptc_get_target</emphasis> to get and print the targets
(using <emphasis>save</emphasis>).
</para>
</listitem>
</itemizedlist>
<para>
In <emphasis>main</emphasis> we iterate through each chain and
for each one we iterate through each rule printing it.
</para>
<para>
The arguments of <emphasis>print_rule</emphasis> are:
</para>
<itemizedlist>
<listitem>
<para>
e = pointer to an <emphasis>ipt_entry</emphasis> structure containing
information about the rule.
</para>
</listitem>
<listitem>
<para>
h = pointer to an <emphasis>iptc_handle_t</emphasis> structure returned by
<emphasis>iptc_init</emphasis>.
</para>
</listitem>
<listitem>
<para>
chain = name of the chain.
</para>
</listitem>
<listitem>
<para>
counters = 0: do not print counters; 1: print them.
</para>
</listitem>
</itemizedlist>
<para>
OK, compile and run program <emphasis>p2</emphasis>:
</para>
<screen>
bash# <command>./ipt-cc p2</command>
bash# <command>./p2</command>
</screen>
<para>
You will get:
</para>
<screen>
INPUT
FORWARD
OUTPUT
chain_1
chain_2
</screen>
<para>
Now modify the environment using <emphasis>iptables</emphasis> to add some rules:
</para>
<screen>
bash# <command>iptables -A INPUT -p tcp -i eth0 -s ! 192.168.1.1 --dport 20 -j ACCEPT</command>
bash# <command>iptables -A chain_1 -p udp -o eth1 -s 192.168.2.0/24 --sport 33 -j DROP</command>
</screen>
<para>
Now if you run again <emphasis>p2</emphasis> you will get:
</para>
<screen>
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
</screen>
<para>
We have now rules printed for <emphasis>INPUT</emphasis> and
<emphasis>chain_1</emphasis> chains. The numbers in the
brackets at left are packet and byte counters respectively.
</para>
</sect2>
<sect2 label="11.10">
<title>iptc_get_policy</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_get_policy
</seg>
<seg>
Get the policy of a given built-in chain.
</seg>
<seg>
const char *iptc_get_policy(const char *chain, struct ipt_counters *counter, iptc_handle_t *handle)
</seg>
<seg>
This function gets the policy of a built-in chain, and fills in the
<emphasis>counters</emphasis> argument with the hit statistics on
that policy.
</seg>
<seg>
You have to pass as arguments the name of the built-in chain you want
to get the policy to, a pointer to an <emphasis>ipt_counters</emphasis>
structure to be filled by the function and the
<emphasis>iptc_handle_t</emphasis> structure identifying the table we are
working to. The <emphasis>ipt_counters</emphasis> structure was explained
in previous section; do not forget that <emphasis>iptc_handle_t</emphasis>
must be obtained by a previous call to the function <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns a char pointer to the policy name.
</seg>
</seglistitem>
</segmentedlist>
<para>
Using pieces of programs 1 and 2 we can write <emphasis>program #3</emphasis>:
</para>
<screen>
/*
* How to use libiptc- program #3
* /usr/local/src/p3.c
*/
#include &#60;getopt.h&#62;
#include &#60;sys/errno.h&#62;
#include &#60;stdio.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;dlfcn.h&#62;
#include &#60;time.h&#62;
#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(&#38;h); chain; chain = iptc_next_chain(&#38;h)) {
if ( !iptc_builtin(chain, h) )
continue;
if ( (policy = iptc_get_policy(chain, &#38;counters, &#38;h)) )
printf("%-10s %-10s [%llu:%llu]\n",
chain, policy, counters.pcnt, counters.bcnt);
}
exit(0);
} /* main */
</screen>
<para>
OK, compile and run program <emphasis>p3</emphasis>:
</para>
<screen>
bash# <command>./ipt-cc p3</command>
bash# <command>./p3</command>
</screen>
<para>
You will get something like this:
</para>
<screen>
BUILT-IN POLICY PKTS-BYTES
----------------------------
INPUT ACCEPT [0:0]
FORWARD ACCEPT [0:0]
OUTPUT ACCEPT [0:0]
</screen>
</sect2>
<sect2 label="11.11">
<title>iptc_read_counter</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_read_counter
</seg>
<seg>
Read counters of a rule in a chain.
</seg>
<seg>
struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
unsigned int rulenum, iptc_handle_t *handle);
</seg>
<seg>
This function read and returns packet and byte counters of the entry
rule in chain <emphasis>chain</emphasis> positioned at
<emphasis>rulenum</emphasis>. Counters are returned in a pointer to a
type structure <emphasis>ipt_counters</emphasis>. Rule numbers start at
1 for the first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be readed; <emphasis>rulenum</emphasis> is an integer value defined the
position in the chain of rules of the rule which counters will be read.
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns a pointer to an <emphasis>ipt_counters</emphasis> structure
containing the byte and packet counters readed.
</seg>
</seglistitem>
</segmentedlist>
</sect2>
</sect1>
<sect1 id="mfunction" label="12">
<title>Functions to modify firewalling rules and statistics</title>
<para>
For those of you who are a little brave, <emphasis>libiptc</emphasis>
has a group of functions to directly modify the firewalling rules and
statistics <emphasis>(use of iptables is really the safest way)</emphasis>.
</para>
<para>
These functions are not covered by this HOWTO and I will limit myself
to presenting improved information taken from
<filename>libiptc.h</filename> and the
<ulink url="http://netfilter.samba.org/documentation/HOWTO/">
Linux netfilter Hacking HOWTO</ulink> by Rusty Russell.
</para>
<sect2 label="12.1">
<title>iptc_commit</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_commit
</seg>
<seg>
Makes the actual changes.
</seg>
<seg>
int iptc_commit(iptc_handle_t *handle)
</seg>
<seg>
The tables that you change are not written back until the
<emphasis>iptc_commit()</emphasis> function is called. This means it
is possible for two library users operating on the same chain to race
each other; locking would be required to prevent this, and it is not
currently done. There is no race with counters, however; counters are
added back in to the kernel in such a way that counter increments
between the reading and writing of the table still show up in the new
table. <emphasis>To protect the status of the system you must commit
your changes</emphasis>.
</seg>
<seg>
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call
to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.2">
<title>iptc_insert_entry</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_insert_entry
</seg>
<seg>
Insert a new rule in a chain.
</seg>
<seg>
int iptc_insert_entry(const ipt_chainlabel chain, const struct ipt_entry *e,
unsigned int rulenum, iptc_handle_t *handle)
</seg>
<seg>
This function insert a rule defined in structure type
<emphasis>ipt_entry</emphasis> in chain <emphasis>chain</emphasis> into
position defined by integer value <emphasis>rulenum</emphasis>. Rule numbers
start at 1 for the first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>e</emphasis> is a pointer to a structure of type
<emphasis>ipt_entry</emphasis> that contains information about the rule to
be inserted. The programmer must fill the fields of this structure with
values required to define his or her rule before passing the pointer as
parameter to the function. <emphasis>rulenum</emphasis> is an integer
value defined the position in the chain of rules where the new rule will
be inserted. Rule numbers start at 1 for the first rule.
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.3">
<title>iptc_replace_entry</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_replace_entry
</seg>
<seg>
Replace an old rule in a chain with a new one.
</seg>
<seg>
int iptc_replace_entry(const ipt_chainlabel chain, const struct ipt_entry *e,
unsigned int rulenum, iptc_handle_t *handle)
</seg>
<seg>
This function replace the entry rule in chain <emphasis>chain</emphasis>
positioned at <emphasis>rulenum</emphasis> with the rule defined in structure
type <emphasis>ipt_entry</emphasis>. Rule numbers start at 1 for the first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>e</emphasis> is a pointer to a structure of type
<emphasis>ipt_entry</emphasis> that contains information about the rule to
be inserted. The programmer must fill the fields of this structure with
values required to define his or her rule before passing the pointer as
parameter to the function. <emphasis>rulenum</emphasis> is an integer
value defined the position in the chain of rules where the old rule will
be replaced by the new one. Rule numbers start at 1 for the first rule.
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.4">
<title>iptc_append_entry</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_append_entry
</seg>
<seg>
Append a new rule in a chain.
</seg>
<seg>
int iptc_append_entry(const ipt_chainlabel chain, const struct ipt_entry *e,
iptc_handle_t *handle)
</seg>
<seg>
This function append a rule defined in structure type
<emphasis>ipt_entry</emphasis> in chain <emphasis>chain</emphasis>
(equivalent to insert with rulenum = length of chain).
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>e</emphasis> is a pointer to a structure of type
<emphasis>ipt_entry</emphasis> that contains information about the rule to
be appended. The programmer must fill the fields of this structure with
values required to define his or her rule before passing the pointer as
parameter to the function. <emphasis>handle</emphasis> is a pointer to a
structure of type <emphasis>iptc_handle_t</emphasis> that was obtained by
a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.5">
<title>iptc_delete_num_entry</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_delete_num_entry
</seg>
<seg>
Delete a rule in a chain.
</seg>
<seg>
int iptc_delete_num_entry(const ipt_chainlabel chain, unsigned int rulenum,
iptc_handle_t *handle)
</seg>
<seg>
This function delete the entry rule in chain <emphasis>chain</emphasis>
positioned at <emphasis>rulenum</emphasis>. Rule numbers start at 1 for the
first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>rulenum</emphasis> is an integer value defined the
position in the chain of rules where the rule will be deleted.
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.6">
<title>iptc_flush_entries</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_flush_entries
</seg>
<seg>
Empty a chain.
</seg>
<seg>
int iptc_flush_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
</seg>
<seg>
This function flushes the rule entries in the given chain (ie. empties chain).
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be flushed; <emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.7">
<title>iptc_zero_entries</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_zero_entries
</seg>
<seg>
Zeroes the chain counters.
</seg>
<seg>
int iptc_zero_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
</seg>
<seg>
This function zeroes the counters in the given chain.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain
which counters will be zero; <emphasis>handle</emphasis> is a pointer
to a structure of type <emphasis>iptc_handle_t</emphasis> that was obtained
by a previous call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.8">
<title>iptc_create_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_create_chain
</seg>
<seg>
Create a new chain.
</seg>
<seg>
int iptc_create_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
</seg>
<seg>
This function create a new chain in the table.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain
to be created; <emphasis>handle</emphasis> is a pointer to a structure
of type <emphasis>iptc_handle_t</emphasis> that was obtained by a previous
call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.9">
<title>iptc_delete_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_delete_chain
</seg>
<seg>
Delete a chain.
</seg>
<seg>
int iptc_delete_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
</seg>
<seg>
This function delete the chain identified by the char pointer
<emphasis>chain</emphasis> in the table.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain
to be deleted; <emphasis>handle</emphasis> is a pointer to a structure
of type <emphasis>iptc_handle_t</emphasis> that was obtained by a previous
call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.10">
<title>iptc_rename_chain</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_rename_chain
</seg>
<seg>
Rename a chain.
</seg>
<seg>
int iptc_rename_chain(const ipt_chainlabel oldname, const ipt_chainlabel newname,
iptc_handle_t *handle)
</seg>
<seg>
This function rename the chain identified by the char pointer
<emphasis>oldname</emphasis> to a new name <emphasis>newname</emphasis>
in the table.
</seg>
<seg>
<emphasis>oldname</emphasis> is a char pointer to the name of the chain
to be renamed, <emphasis>newname</emphasis> is the new name;
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous
call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.11">
<title>iptc_set_policy</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_set_policy
</seg>
<seg>
Set the policy in a built-in chain.
</seg>
<seg>
int iptc_set_policy(const ipt_chainlabel chain, const ipt_chainlabel policy,
struct ipt_counters *counters, iptc_handle_t *handle)
</seg>
<seg>
This function set the policy in chain <emphasis>chain</emphasis> to the
value represented by the char pointer <emphasis>policy</emphasis>. If you
want to set at the same time the counters of the chain, fill those values
in a structure of type <emphasis>ipt_counters</emphasis> and pass a pointer
to it as parameter <emphasis>counters</emphasis>. Be careful: the chain
<emphasis>must be</emphasis> a built-in chain.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>policy</emphasis> is a char pointer to the name of
the policy to be set. <emphasis>counters</emphasis> is a pointer to an
<emphasis>ipt_counters</emphasis> structure to be used to set the counters
of the chain. <emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.12">
<title>iptc_zero_counter</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_zero_counter
</seg>
<seg>
Zero counters of a rule in a chain.
</seg>
<seg>
int iptc_zero_counter(const ipt_chainlabel chain, unsigned int rulenum,
iptc_handle_t *handle)
</seg>
<seg>
This function zero packet and byte counters of the entry rule in chain
<emphasis>chain</emphasis> positioned at <emphasis>rulenum</emphasis>.
Rule numbers start at 1 for the first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>rulenum</emphasis> is an integer value defined the
position in the chain of rules of the rule which counters will be zero.
<emphasis>handle</emphasis> is a pointer to a structure of type
<emphasis>iptc_handle_t</emphasis> that was obtained by a previous call to
<emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
<sect2 label="12.13">
<title>iptc_set_counter</title>
<segmentedlist>
<segtitle>Name</segtitle>
<segtitle>Usage</segtitle>
<segtitle>Prototype</segtitle>
<segtitle>Description</segtitle>
<segtitle>Parameters</segtitle>
<segtitle>Returns</segtitle>
<seglistitem>
<seg>
iptc_set_counter
</seg>
<seg>
Set counters of a rule in a chain.
</seg>
<seg>
int iptc_set_counter(const ipt_chainlabel chain, unsigned int rulenum,
struct ipt_counters *counters, iptc_handle_t *handle)
</seg>
<seg>
This function set packet and byte counters of the entry rule in chain
<emphasis>chain</emphasis> positioned at <emphasis>rulenum</emphasis>
with values passed in a type structure <emphasis>ipt_counters</emphasis>.
Rule numbers start at 1 for the first rule.
</seg>
<seg>
<emphasis>chain</emphasis> is a char pointer to the name of the chain to
be modified; <emphasis>rulenum</emphasis> is an integer value defined the
position in the chain of rules of the rule which counters will be set.
<emphasis>counters</emphasis> is a pointer to an
<emphasis>ipt_counters</emphasis> structure to be used to set the counters
of the rule; the programmer must fill the fields of this structure with
values to be set. <emphasis>handle</emphasis> is a pointer to a structure
of type <emphasis>iptc_handle_t</emphasis> that was obtained by a previous
call to <emphasis>iptc_init</emphasis>.
</seg>
<seg>
Returns integer value 1 (true) if successful; returns integer value 0
(false) if fails. In this case <emphasis>errno</emphasis> is set to the
error number generated. Use <emphasis>iptc_strerror</emphasis> to get a
meaningful information about the problem. If errno == 0, it means there
was a version error (ie. upgrade <emphasis>libiptc</emphasis>).
</seg>
</seglistitem>
</segmentedlist>
</sect2>
</sect1>
<sect1 id="bmeter" label="13">
<title>Bandwidth meter</title>
<para>
In this chapter I am going to develop a simple bandwidth meter using
the following functions from <emphasis>libiptc</emphasis>:
</para>
<itemizedlist>
<listitem>
<para>
To initialize the system:
<emphasis>iptc_handle_t iptc_init(const char *tablename)</emphasis>.
</para>
</listitem>
<listitem>
<para>
To catch from errors:
<emphasis>const char *iptc_strerror(int err)</emphasis>.
</para>
</listitem>
<listitem>
<para>
To iterate through the chains of the table:
<emphasis>const char *iptc_first_chain(iptc_handle_t *handle)</emphasis> and
<emphasis>const char *iptc_next_chain(iptc_handle_t *handle)</emphasis>.
</para>
</listitem>
<listitem>
<para>
To read packet and byte counters for a specific rule:
<emphasis>struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
unsigned int rulenum, iptc_handle_t *handle)</emphasis>.
</para>
</listitem>
</itemizedlist>
<para>
Also the function <emphasis>gettimeofday</emphasis> will be used to
measure elapsed time and the function <emphasis>getopt</emphasis> to
get options from the command line.
</para>
<para>
I don't know really if the term <emphasis>bandwidth meter</emphasis> is
well used here. I interpret <emphasis>bandwidth</emphasis> as a reference to
a flow capacity; perhaps a better term could be <emphasis>flow meter</emphasis>.
</para>
<para>
Here is the <emphasis>bandwidth meter</emphasis> <filename>bw.c</filename>.
It's well commented to be easy followed by everyone:
</para>
<screen>
/*
* How to use libiptc- program #4
* /usr/local/src/bw.c
* By Leonardo Balliache - 04.09.2002
* e-mail: leonardo@opalsoft.net
* --WELL COMMENTED-- to be easy followed by everyone.
*/
/* include files required */
#include &#60;getopt.h&#62;
#include &#60;sys/errno.h&#62;
#include &#60;sys/time.h&#62;
#include &#60;stdio.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;dlfcn.h&#62;
#include &#60;time.h&#62;
#include &#60;unistd.h&#62;
#include "libiptc/libiptc.h"
#include "iptables.h"
/* colors to differentiate chains measures */
#define RED "\033[41m\033[37m"
#define GREEN "\033[42m\033[30m"
#define ORANGE "\033[43m\033[30m"
#define BLUE "\033[44m\033[37m"
#define MAGENTA "\033[45m\033[37m"
#define CYAN "\033[46m\033[30m"
#define WHITE "\033[47m\033[30m"
#define BLACK "\033[40m\033[37m"
#define RESET "\033[00m"
/* maximum number of chains to be processed */
#define MAXUSERCHAINS 7
/* time between measures in seconds; adjust as you like */
#define SLEEPTIME 1
/* structure to count bytes per chain */
struct bwcnt {
int start; /* the chain was initialized */
u_int64_t icnt; /* bytes through; previous measure */
u_int64_t ocnt; /* bytes through; current measure */
double bw; /* bandwitdh (flow) on this chain */
};
/* function to calculate differences of time in seconds.
* micro-seconds precision.
*/
double delta(struct timeval a, struct timeval b)
{
if (a.tv_usec &#38; b.tv_usec) {
a.tv_sec--;
a.tv_usec += 1000000;
}
return a.tv_sec-b.tv_sec + (a.tv_usec-b.tv_usec)/1000000.0;
}
/* main function */
int main(int argc, char *argv[])
{
int i, j, ok;
double totbw;
iptc_handle_t h;
int c, act_bw = 0;
const char *chain = NULL;
const char *tablename = "filter";
struct timeval ti, to;
struct bwcnt bw[MAXUSERCHAINS];
struct ipt_counters *counters;
char *col[9] = { RED,GREEN,ORANGE,BLUE,MAGENTA,CYAN,WHITE,BLACK,RESET };
program_name = "bw";
program_version = NETFILTER_VERSION;
/* check options
* we have 2 options:
* -c = display current flow (each SLEEPTIME).
* -a = display average flow (from start); default option.
*/
while ((c = getopt (argc, argv, "ac")) != -1)
switch (c) {
case 'a':
act_bw = 0;
break;
case 'c':
act_bw = 1;
break;
case '?':
if (isprint(optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,"Unknown option character `\\x%x'.\n",optopt);
exit(1);
default:
abort();
}
/* initialize array of chains */
memset(&#38;bw, 0, MAXUSERCHAINS * sizeof(struct bwcnt));
/* get time to start meter on variable ti */
gettimeofday(&#38;ti, NULL);
/* fire meter loop */
if ( act_bw )
printf("Displaying current flow values ...\n");
else
printf("Displaying average flow values ...\n");
/* forever loop; abort the program with ^C */
while ( 1 ) {
/* you have to initialize for each measure to be done */
if ( !(h = iptc_init(tablename)) ) {
printf("Error initializing: %s\n", iptc_strerror(errno));
exit(errno);
}
ok = 0; /* we start a new loop */
gettimeofday(&#38;to, NULL); /* have a time shoot */
/* iterate through each chain of the table */
for (chain = iptc_first_chain(&#38;h), i = 0;
chain;
chain = iptc_next_chain(&#38;h)) {
if ( iptc_builtin(chain, h) )
continue; /* if it is a built-in chain, ignore it */
/* ok, read the counters of this chain */
if ( !(counters = iptc_read_counter(chain, 0, &#38;h)) ) {
printf("Error reading %s: %s\n", chain, iptc_strerror(errno));
exit(errno);
}
/* check that we do not have more chains than we can process */
if ( i >= MAXUSERCHAINS ) {
printf("Maximum of %d user chains exceeded!!\n", MAXUSERCHAINS);
exit(1);
}
/* this chain counter has not been initialized; initialize it */
if ( bw[i].start == 0 ) {
bw[i].icnt = counters->bcnt;
bw[i].start = 1;
}
/* this chain has a previous measure; take the current one */
else {
bw[i].ocnt = counters->bcnt;
if ( bw[i].ocnt == bw[i].icnt ) /* no new bytes flowing? */
bw[i].bw = 0; /* flow is zero */
else
/* flow in this chain is:
* current bytes count (bw[i].octn) *minus*
* previous bytes count (bw[i].icnt) *divided by*
* 128.0 to convert bytes to kbits *and divided by*
* difference in times in seconds *to get*
* flow in kbits/sec that is what we want.
*/
bw[i].bw = (bw[i].ocnt - bw[i].icnt) / (128.0 * delta(to, ti));
/* do you want current flow of this chain? initialize previous
* bytes count to current bytes count; we get the flow in last
* SLEEPTIME elapsed time.
*/
if ( act_bw )
bw[i].icnt = bw[i].ocnt;
ok = 1; /* ok, we have some measure to show */
}
++i; /* next chain, please */
}
/* we iterate and i == 0; we do not have user chains at all */
if ( i == 0 ) {
printf("No user chains to meter!!\n");
exit(1);
}
/* do you want to measure current flow? initialize previous time
* to actual time; we get the time elapsed in last SLEEPTIME.
*/
if ( act_bw )
ti = to;
/* do we have something to show? ok, display it */
if ( ok ) {
totbw = 0;
for ( j = 0; j &#60; i; ++j ) {
totbw = totbw + bw[j].bw; /* calculate total flow */
}
printf("%s%6.1fk:%s ", col[7], totbw, col[8]); /* display total */
for ( j = 0; j &#60; i; ++j ) { /* display flow of each chain in color */
printf("%s%6.1fk%s ", col[j], bw[j].bw, col[8]);
}
printf("\n");
}
sleep(SLEEPTIME); /* rest a little; you go too fast */
} /* give us enough time in order to let some bytes flow */
exit(0); /* bye, we have our measures!! */
} /* main */
</screen>
<para>
Write your program and compile as before:
</para>
<screen>
bash# <command>./ipt-cc bw</command>
</screen>
<para>
Before using the meter we need to set our environment.
</para>
<para>
First, we have to have at least 2 PCs connected in a network. This is our
diagram configuration:
</para>
<screen>
+--------+ eth0 eth0 +--------+
| PC #1 +-----------------+ PC #2 |
+--------+ +--------+
eth0=192.168.1.1 eth0=192.168.1.2
</screen>
<para>
Second, we need to install a very nice and useful package called
<emphasis>netcat</emphasis> written by Hobbit. This
<emphasis>excellent</emphasis> package will help us to inject and receive
a flow of bytes between 2 NICs. If you don't have the package in your
system, download it from <ulink url="http://rr.sans.org/audit/netcat.php">
http://rr.sans.org/audit/netcat.php</ulink>.
</para>
<para>
The version that I use is <emphasis>1.10-277</emphasis>. To install it follow
these instructions:
</para>
<screen>
bash# <command>cp netcat-1.10.tar.gz /usr/local/src</command>
bash# <command>tar xzvf netcat-1.10.tar.gz</command>
bash# <command>cd netcat-1.10</command>
</screen>
<para>
My version requires to make a patch first; check yours if you have a file
with a <emphasis>.dif</emphasis> extension and apply it too:
</para>
<screen>
bash# <command>patch -p0 -i netcat-1.10.dif</command>
</screen>
<para>
Next compile the package using <emphasis>make</emphasis>:
</para>
<screen>
bash# <command>make linux</command>
</screen>
<para>
Copy the binary <emphasis>nc</emphasis> to your user bin directory:
</para>
<screen>
bash# <command>cp nc /usr/bin</command>
</screen>
<para>
And also to the second PC in your network:
</para>
<screen>
bash# <command>scp nc 192.168.1.2:/usr/bin</command>
</screen>
<para>
We are going to use <emphasis>netcat</emphasis> to
<quote>listen</quote> to a flow of bytes from PC #2 and
to <quote>talk</quote> from PC #1. Using tty1 to tty4
consoles on PC #2 let's start <emphasis>netcat</emphasis> to
listen from this PC. Go to PC #2 and in tty1 type:
</para>
<screen>
bash# <command>nc -n -v -l -s 192.168.1.2 -p 1001 >/dev/null</command>
</screen>
<para>
<emphasis>netcat</emphasis> must respond with:
</para>
<screen>
listening on [192.168.1.2] 1001 ...
</screen>
<para>
This command started <emphasis>netcat</emphasis> to listen from address
<emphasis>192.168.1.2</emphasis> using port number <emphasis>1001</emphasis>.
Arguments are: <emphasis>-n</emphasis> = use numeric address identification;
<emphasis>-v</emphasis> = verbose; <emphasis>-l</emphasis> = listen. All the
flow that <emphasis>netcat</emphasis> receives in
<emphasis>192.168.1.2:1001</emphasis> will be redirected to the
<quote>black hole</quote> in <filename>/dev/null</filename>.
</para>
<para>
Repeat the command in tty2, tty3 and tty4; change to tty2 using
<keycap>ALT-F2</keycap> and after logging in write:
</para>
<screen>
bash# <command>nc -n -v -l -s 192.168.1.2 -p 1002 >/dev/null</command>
</screen>
<para>
Now we are <quote>listening</quote> to the same address but port
number <emphasis>1002</emphasis>.
</para>
<para>
Go on now with tty3:
</para>
<screen>
bash# <command>nc -n -v -l -s 192.168.1.2 -p 1003 >/dev/null</command>
</screen>
<para>
And tty4:
</para>
<screen>
bash# <command>nc -n -v -l -s 192.168.1.2 -p 1004 >/dev/null</command>
</screen>
<para>
Now we are listening in PC #2, address
<emphasis>192.168.1.2</emphasis> in ports <emphasis>1001</emphasis>,
<emphasis>1002</emphasis>, <emphasis>1003</emphasis> and
<emphasis>1004</emphasis>.
</para>
<para>
Come back to PC #1 and let's set the environment to allow
<emphasis>iptables</emphasis> to help us to complete our tests:
</para>
<para>
On PC #1, type the into tty1 as follows:
</para>
<screen>
bash# <command>iptables -F</command>
bash# <command>iptables -X</command>
bash# <command>iptables -N chn_1</command>
bash# <command>iptables -N chn_2</command>
bash# <command>iptables -N chn_3</command>
bash# <command>iptables -N chn_4</command>
bash# <command>iptables -A chn_1 -j ACCEPT</command>
bash# <command>iptables -A chn_2 -j ACCEPT</command>
bash# <command>iptables -A chn_3 -j ACCEPT</command>
bash# <command>iptables -A chn_4 -j ACCEPT</command>
bash# <command>iptables -A OUTPUT -o eth0 -p tcp --dport 1001 -j chn_1</command>
bash# <command>iptables -A OUTPUT -o eth0 -p tcp --dport 1002 -j chn_2</command>
bash# <command>iptables -A OUTPUT -o eth0 -p tcp --dport 1003 -j chn_3</command>
bash# <command>iptables -A OUTPUT -o eth0 -p tcp --dport 1004 -j chn_4</command>
</screen>
<para>
These commands will:
</para>
<itemizedlist>
<listitem>
<para>
Flush all chains in table <emphasis>filter</emphasis>.
</para>
</listitem>
<listitem>
<para>
Delete all user chains in table <emphasis>filter</emphasis>.
</para>
</listitem>
<listitem>
<para>
Create user chains <emphasis>chn_1</emphasis>,
<emphasis>chn_2</emphasis>, <emphasis>chn_3</emphasis> and
<emphasis>chn_4</emphasis>.
</para>
</listitem>
<listitem>
<para>
Establish a target <emphasis>ACCEPT</emphasis> in each user chain.
</para>
</listitem>
<listitem>
<para>
Create 4 rules in chain <emphasis>OUTPUT</emphasis> that matches
port numbers <emphasis>1001</emphasis> to
<emphasis>1004</emphasis> and target it to user chains
<emphasis>chn_1</emphasis> to <emphasis>chn_4</emphasis>.
</para>
</listitem>
</itemizedlist>
<para>
Now start the <emphasis>bw</emphasis> meter using current values:
</para>
<screen>
bash# <command>./bw -c</command>
</screen>
<para>
It must respond with:
</para>
<screen>
Displaying current flow values ...
0.0k: 0.0k 0.0k 0.0k 0.0k
0.0k: 0.0k 0.0k 0.0k 0.0k
0.0k: 0.0k 0.0k 0.0k 0.0k
0.0k: 0.0k 0.0k 0.0k 0.0k
</screen>
<para>
It informs that measures are current flows. Every line is a measure
taken each <emphasis>SLEEPTIME</emphasis> lapse (1 second in our
program). First column (in black) are total flow, next columns (in red,
green, orange and blue) are flows in chains <emphasis>chn_1</emphasis>,
<emphasis>chn_2</emphasis>, <emphasis>chn_3</emphasis> and
<emphasis>chn_4</emphasis> respectively. Of course we do not have any
flow now. However let <emphasis>bw</emphasis> run and continue reading.
</para>
<para>
Let's start now one of our byte flows; go to tty2 in PC #1 with
<keycap>ALT-F2</keycap> and after logging in, type:
</para>
<screen>
bash# <command>yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2001 192.168.1.2 1001</command>
</screen>
<para>
<emphasis>netcat</emphasis> responds with:
</para>
<screen>
(UNKNOWN) [192.168.1.2] 1000 (?) open
</screen>
<para>
Now we have a flow of bytes from PC #1 to PC #2. <emphasis>yes</emphasis>
generates a constant flow of zeroes; this flow is piped to
<emphasis>netcat</emphasis> through address <emphasis>192.168.1.1</emphasis>,
port <emphasis>2001</emphasis> and sends it to PC #2, address
<emphasis>192.168.1.2</emphasis>, port <emphasis>1001</emphasis>
(where PC #2 is listening).
</para>
<para>
Check now the display of <emphasis>bw</emphasis> in tty1:
</para>
<screen>
7653.2k: 7653.2k 0.0k 0.0k 0.0k
7829.5k: 7829.5k 0.0k 0.0k 0.0k
7786.7k: 7786.7k 0.0k 0.0k 0.0k
7892.1k: 7982.1k 0.0k 0.0k 0.0k
</screen>
<para>
Your mileage can vary depending of the physical characteristics of your
system. In mine I have a flow of aproximately 7700 kbits/sec in the first
chain <emphasis>chn_1</emphasis> which corresponds to port number
<emphasis>1001</emphasis> in PC #2.
</para>
<para>
Let's start now the second bytes flow; go to tty3 in PC #1 with
<keycap>ALT-F3</keycap> and after logging in, type:
</para>
<screen>
bash# <command>yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2002 192.168.1.2 1002</command>
</screen>
<para>
<emphasis>netcat</emphasis> responds with:
</para>
<screen>
(UNKNOWN) [192.168.1.2] 1002 (?) open
</screen>
<para>
Now we have 2 flows of bytes from PC #1 to PC #2; one from
<emphasis>192.168.1.1:2001</emphasis> to
<emphasis>192.168.1.2:1001</emphasis> and another from
<emphasis>192.168.1.1:2002</emphasis> to
<emphasis>192.168.1.2:1002</emphasis>.
</para>
<para>
Now check the display of <emphasis>bw</emphasis> in tty1:
</para>
<screen>
7819.6k: 4144.2k 3675.4k 0.0k 0.0k
8090.5k: 3923.9k 4166.6k 0.0k 0.0k
7794.7k: 3920.8k 3873.9k 0.0k 0.0k
7988.3k: 3754.6k 4233.7k 0.0k 0.0k
</screen>
<para>
Now we have 2 flows; each of them is more or less 50% of the total flow
going out of the computer. The Linux kernel tries to balance the bandwidth
available between the 2 channels of output.
</para>
<para>
To continue, start the 2 aditional flows through channels
<emphasis>192.168.1.1:2003-192.168.1.2:1003</emphasis> and
<emphasis>192.168.1.1:2004-192.168.1.2:1004</emphasis>.
</para>
<para>
In tty4 type:
</para>
<screen>
bash# <command>yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2003 192.168.1.2 1003</command>
</screen>
<para>
In tty5 type:
</para>
<screen>
bash# <command>yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2004 192.168.1.2 1004</command>
</screen>
<para>
The display of <emphasis>bw</emphasis> in tty1 will be something like:
</para>
<screen>
8120.6k: 1705.3k 2354.9k 1898.6k 2161.8k
7765.3k: 1634.2k 2560.2k 2011.4k 1559.5k
7911.9k: 1699.8k 2090.3k 1768.0k 2353.8k
8309.4k: 1734.5k 1999.7k 1999.9k 2575.3k
</screen>
<para>
Total bandwidth is distributed between the 4 channels of flow.
</para>
</sect1>
<sect1 id="cflows" label="14">
<title>Controlling flows</title>
<para>
In this chapter we are going to try to control the flows using the Linux
kernel queue disciplines. Perhaps, depending on how you compiled your
kernel, you will again need to run <command>make menuconfig</command>,
re-configure your options, re-compile and re-install your kernel.
</para>
<para>
This chapter <emphasis>is not</emphasis> and
<emphasis>does not pretend to be</emphasis> a tutorial about the
implementation of <emphasis>QoS (Quality of Service)</emphasis> in Linux.
If you don't have previous experience with <emphasis>QoS</emphasis>
it's better to read some references at the end of this document to acquire
the concepts required for <emphasis>QoS</emphasis> implementation.
</para>
<para>
With this advice, I'm not going to explain in detail each of the
commands needed to control flows in Linux because it is not the goal of
this HOWTO. However, the implementation of some of these techniques will
serve us to show the bandwidth meter (based on <emphasis>libiptc</emphasis>)
behaviour.
</para>
<para>
First check if you have QoS implementation options implemented in your
kernel. Run <command>make menuconfig</command>, follow the menu to
<emphasis>Networking options</emphasis> and look for last menu of this
option <emphasis>QoS and/or fair queueing</emphasis>. Here use (or check
if they are active) these options:
</para>
<screen>
[*] QoS and/or fair queueing
&#60;M&#62; CBQ packet scheduler
&#60;M&#62; CSZ packet scheduler
[*] ATM pseudo-scheduler
&#60;M&#62; The simplest PRIO pseudoscheduler
&#60;M&#62; RED queue
&#60;M&#62; SFQ queue
&#60;M&#62; TEQL queue
&#60;M&#62; TBF queue
&#60;M&#62; GRED queue
&#60;M&#62; Diffserv field marker
&#60;M&#62; Ingress Qdisc
[*] QoS support
[*] Rate estimator
[*] Packet classifier API
&#60;M&#62; TC index classifier
&#60;M&#62; Routing table based classifier
&#60;M&#62; Firewall based classifier
&#60;M&#62; U32 classifier
&#60;M&#62; Special RSVP classifier
&#60;M&#62; Special RSVP classifier for IPv6
[*] Traffic policing (needed for in/egress)
</screen>
<para>
Save your configuration, recompile your kernel and modules, and
re-install it. We are going to use the
<emphasis>CBQ packet scheduler</emphasis> to implement some queues
to control bytes flow in our PC #1 NIC.
</para>
<para>
Personally I preferred the excellent <emphasis>HTB queueing
discipline implementation</emphasis> by Martin Devera but actually this
implementation is not in standard Linux (but it will be); for
implementing it you have to patch your kernel before recompiling and
it's better not to complicate things more. However I have to say that
this queue discipline is a lot more simple to use than
<emphasis>CBQ</emphasis> happens to be. More information on
<emphasis>HTB queueing discipline</emphasis> are linked at the end of
this document.
</para>
<para>
Having compiled and re-installed your kernel you have to install the
<emphasis>iproute2</emphasis> package that will be used to run the
commands needed to implement the queues. Download this package from
<ulink url="ftp://ftp.inr.ac.ru/ip-routing">ftp://ftp.inr.ac.ru/ip-routing</ulink>.
</para>
<para>
I'm working with version <emphasis>2.2.4-now-ss001007</emphasis>. To install
it follow these instructions:
</para>
<screen>
bash# <command>cp iproute2-2.2.4-now-ss001007.tar.gz /usr/local/src</command>
bash# <command>tar xzvf iproute2-2.2.4-now-ss001007.tar.gz</command>
bash# <command>cd iproute2</command>
bash# <command>make</command>
</screen>
<para>
After <emphasis>make</emphasis> compiles the <emphasis>iproute2</emphasis>
package successfully the <emphasis>ip</emphasis> utility will be in
<filename>iproute2/ip</filename> directory and the
<emphasis>tc</emphasis> utility in <filename>iproute2/tc</filename>
directory. Copy both of them to <filename>/usr/bin</filename> directory:
</para>
<screen>
bash# <command>cp ip/ip /usr/bin</command>
bash# <command>cp tc/tc /usr/bin</command>
</screen>
<para>
Now, using the <emphasis>tc</emphasis> utility, we are going to create a
<emphasis>CBQ</emphasis> queue in the interface <emphasis>eth0</emphasis> of
the PC #1 computer. This queue will have 4 classes as children and each of
these classes will be used to control the 4 flows from
<emphasis>192.168.1.1</emphasis> to <emphasis>192.168.1.2</emphasis>
through ports <emphasis>1001</emphasis> to <emphasis>1004</emphasis>.
</para>
<para>
Write and run the following commands:
</para>
<screen>
bash# <command>tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 10Mbit \
avpkt 1000 cell 8</command>
</screen>
<para>
This command creates the main (root) cbq queue 1:0 in the
<emphasis>eth0</emphasis> interface; the bandwidth of this queue is
10Mbit/sec corresponding to our Ethernet interface.
</para>
<para>
Now write and run:
</para>
<screen>
bash# <command>tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit \
rate 1000kbit prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded</command>
</screen>
<para>
This command create the main cbq class 1:1. The rate of this class
will be 1000kbit/sec.
</para>
<para>
Now we are going to create 4 classes ownned by this class; the classes
will have rates of 100kbit, 200kbit, 300kbit and 400kbit respectively.
Write and run these commands:
</para>
<screen>
bash# <command>tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 10Mbit \
rate 100kbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000</command>
bash# <command>tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 10Mbit \
rate 200kbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000</command>
bash# <command>tc class add dev eth0 parent 1:1 classid 1:5 cbq bandwidth 10Mbit \
rate 300kbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000</command>
bash# <command>tc class add dev eth0 parent 1:1 classid 1:6 cbq bandwidth 10Mbit \
rate 400kbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000</command>
</screen>
<para>
Each of these classes will have a <emphasis>sfq</emphasis> queue discipline
attached to them to dispatch their packets. Write and run these commands:
</para>
<screen>
bash# <command>tc qdisc add dev eth0 parent 1:3 handle 30: sfq perturb 15</command>
bash# <command>tc qdisc add dev eth0 parent 1:4 handle 40: sfq perturb 15</command>
bash# <command>tc qdisc add dev eth0 parent 1:5 handle 50: sfq perturb 15</command>
bash# <command>tc qdisc add dev eth0 parent 1:6 handle 60: sfq perturb 15</command>
</screen>
<para>
These commands create 4 <emphasis>sfq</emphasis> queue disciplines, one
for each class. <emphasis>sfq</emphasis> queue discipline is some kind of
<emphasis>fair controlling queue</emphasis>. It tries to give to each connection
in an interface same oportunity to their packets to be dispatched to at all.
</para>
<para>
Finally we are going to create filters to assign flows to ports
<emphasis>1001</emphasis>, <emphasis>1002</emphasis>, <emphasis>1003</emphasis>
and <emphasis>1004</emphasis> to classes <emphasis>1:3</emphasis>,
<emphasis>1:4</emphasis>, <emphasis>1:5</emphasis> and
<emphasis>1:6</emphasis> respectively. Write and run as follows:
</para>
<screen>
bash# <command>tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
dport 1001 0xffff flowid 1:3</command>
bash# <command>tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
dport 1002 0xffff flowid 1:4</command>
bash# <command>tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
dport 1003 0xffff flowid 1:5</command>
bash# <command>tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip \
dport 1004 0xffff flowid 1:6</command>
</screen>
<para>
After running all these commands, now check your <emphasis>bw</emphasis>
meter (you must be running <emphasis>netcat</emphasis> listening at ports
<emphasis>1001</emphasis> to <emphasis>1004</emphasis> in PC #2 and
talking in PC #1 as was explained in previous chapter and <emphasis>bw</emphasis>
running in <emphasis>current -c</emphasis> mode). You will have something
like this:
</para>
<screen>
Current flow values ...
1099.9k: 108.8k 196.5k 337.9k 456.8k
1104.2k: 115.3k 184.9k 339.9k 464.1k
1102.1k: 117.3k 174.7k 339.7k 470.5k
1114.4k: 113.6k 191.7k 340.7k 468.4k
1118.4k: 113.7k 194.3k 340.5k 469.9k
</screen>
<para>
<emphasis>bw</emphasis> show us how flows are controlling using queue
disciplines of the Linux kernel. As you see,
<emphasis>CBQ queue discipline</emphasis> is not a very precise queue but
you more or less have a flow of approximately
<emphasis>1000=100+200+300+400</emphasis> on interface
<emphasis>eth0</emphasis>.
</para>
<para>
To step back, write and run as follows:
</para>
<screen>
bash# <command>tc qdisc del dev eth0 root handle 1:0 cbq</command>
</screen>
<para>
on PC #1, to delete the main (root) queue discipline and owned classes
and filters.
</para>
<screen>
bash# <command>killall nc</command>
</screen>
<para>
on PC #2 and PC #1, to stop <emphasis>netcat</emphasis>.
</para>
<screen>
bash# <command>iptables -F</command>
bash# <command>iptables -X</command>
</screen>
<para>
on PC #1, to clear <emphasis>iptables</emphasis> rules and chains.
</para>
<screen>
bash# <command>Ctrl-C</command>
</screen>
<para>
on PC #1, tty1 to stop <emphasis>bw</emphasis> bandwidth meter.
</para>
</sect1>
<sect1 id="somelinks" label="15">
<title>Some interesting links</title>
<orderedlist>
<listitem>
<para>
<ulink url="http://netfilter.samba.org/">
iptables-1.2.6 by Paul Russell</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://netfilter.samba.org/documentation/HOWTO/">
Linux netfilter Hacking HOWTO by Paul Russell</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.linuxgrill.com/iproute2-toc.html">
iproute2 by Alexey Kuznetsov</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.tldp.org/HOWTO/Adv-Routing-HOWTO.html">
Advance routing Linux HOWTO</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://luxik.cdi.cz/~devik/qos/htb/htbtheory.htm">
HTB queueing discipline implementation by Martin Devera</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://qos.ittc.ukans.edu/howto/howto.html">
Linux-Advance Networking Overview by Saravanan Radhakrishnan</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.docum.org/">
monitor.pl by Stef Coene</ulink>.
</para>
</listitem>
<listitem>
<para>
<ulink url="http://rr.sans.org/audit/netcat.php">
netcat by Hobbit</ulink>.
</para>
</listitem>
</orderedlist>
</sect1>
<sect1 id="author" label="16">
<title>About the author</title>
<para>
Leonardo Balliache is a power electrical engineer that left high voltage
lines, transformers and protection relays in 1983 to dedicated full of his
time to computer sciences.
</para>
<para>
He is the General Manager of OpalSoft, a venezuelan company dedicated to
business packages software development.
</para>
<para>
In 1989 he started learning Unix using Coherent operating system. After
this he was interested in Linux and specially in bandwidth bottleneck
problems, bandwidth controlling, packet filtering and hierarching, Linux
QoS (Quality of Service), advanced routing, network protection, firewalling,
private network connection through the Internet and solving line and server
load balancing problems.
</para>
<para>
His company will be opening a new area of business offering Linux
QoS solution implementations in Venezuela.
</para>
<para>
Married to Cielo, with 3 sons (Jose, Dario, Gustavo), he can be reached at
<ulink url="mailto:leonardo@opalsoft.net">leonardo@opalsoft.net</ulink>.
He is working now (please be patient) to open a QoS Linux information site
at <ulink url="http://opalsoft.net/qos/">
http://opalsoft.net/qos/</ulink> to interchange knowledge with people
interested and to make his works in the Linux <quote>best of all</quote>
operating system available to the public.
</para>
<para>
April 30, 2002
</para>
<para>
Caracas, Venezuela
</para>
</sect1>
</article>