![]() | ![]() |
|
Querying libiptc HOWTOLeonardo Balliacheleonardo@opalsoft.net Version 0.1 - April 30, 2002
1. Legal NoticeThis 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 here. 2. TranslationsIf you want to translate this document you are free to do so. However, you will need to do the following:
Thank for your translation. 3. DisclaimerI took this "disclaimer" from Linux-Advance Networking Overview by Saravanan Radhakrishnan (08-1999) because it applies in my own case: 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. 4. CreditsI want to thank the following people and organizations who had helped me, directly or not, to make this document possible:
5. ObjectivesThis HOWTO explains how to use the libiptc library included in the iptables 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 "brave", to modify them. You can find the latest version of this document at Querying libiptc HOWTO.html. If you have suggestions to help make this document better, please submit your ideas to me at the following address: leonardo@opalsoft.net. 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 monitor.pl, a simple perl program for bandwith measurement, written by Stef Coene at http://www.docum.org. I recommend this site to people interested in bandwidth control and measurement. 6. What is libiptc?libiptc is the library that is used to communicate with netfilter, the internal kernel code in charge of firewalling and packet filtering. This code and iptables were written by Paul "Rusty" Russell. iptables was developed using libiptc calls to get the job done. If you want to have more information about iptables, libiptc and the firewalling code, have a look at links at the end of this document. 7. How did I obtain this knowledge?Just looking at code in iptables 1.2.6 package and especially at program iptables-save.c that use libiptc to dump information from firewalling kernel code. I will try to be very pragmatic and clear in order to make this HOWTO useful. 8. Previous knowledge and system requirementsYou have to have some previous knowledge to follow this document:
9. Installing iptables + libiptcTo install libiptc follow these steps:
10. How to create your program(s)Create your program(s) in /usr/local/src; this way you will not have problems with gcc looking for files in the "include" section. Your program(s) would be something like this:
11. Functions to query libiptcThis 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. 11.1. iptc_initName: 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:
11.2. iptc_strerrorName: 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. 11.3. iptc_first_chainName: 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. 11.4. iptc_next_chainName: 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:
Write this program and save it as p1.c in /usr/local/src. Now write this "bash" script to simplify the compiling process:
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:
You will get:
Try to generate an error initializing tablename to myfilter instead of filter. When you compile and execute your program again, you will get:
iptables informs you that myfilter does not exist as a table. 11.5. iptc_is_chainName: 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. 11.6. iptc_builtinName: 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. 11.7. iptc_first_ruleName: 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. 11.8. iptc_next_ruleName: 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. 11.9. iptc_get_targetName: 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:
An ipt_entry structure contains:
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:
The function print_rule borrowed from iptables-save.c prints the information about a rule into a readable form using:
In main we iterate through each chain and for each one we iterate through each rule printing it. The arguments of print_rule are:
OK, compile and run program p2:
You will get:
Now modify the environment using iptables to add some rules:
Now if you run again p2 you will get:
We have now rules printed for INPUT and chain_1 chains. The numbers in the brackets at left are packet and byte counters respectively. 11.10. iptc_get_policyName: 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:
OK, compile and run program p3:
You will get something like this:
11.11. iptc_read_counterName: 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. 12. Functions to modify firewalling rules and statisticsFor those of you who are a little brave, libiptc has a group of functions to directly modify the firewalling rules and statistics (use of iptables is really the safest way). These functions are not covered by this HOWTO and I will limit myself to presenting improved information taken from libiptc.h and the Linux netfilter Hacking HOWTO by Rusty Russell. 12.1. iptc_commitName: iptc_commit Usage: Makes the actual changes. Prototype: int iptc_commit(iptc_handle_t *handle) Description: The tables that you change are not written back until the iptc_commit() 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. To protect the status of the system you must commit your changes. Parameters: 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.2. iptc_insert_entryName: iptc_insert_entry Usage: Insert a new rule in a chain. Prototype: int iptc_insert_entry(const ipt_chainlabel chain, const struct ipt_entry *e, unsigned int rulenum, iptc_handle_t *handle) Description: This function insert a rule defined in structure type ipt_entry in chain chain into position defined by integer value rulenum. Rule numbers start at 1 for the first rule. Parameters: chain is a char pointer to the name of the chain to be modified; e is a pointer to a structure of type ipt_entry 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. rulenum 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. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.3. iptc_replace_entryName: iptc_replace_entry Usage: Replace an old rule in a chain with a new one. Prototype: int iptc_replace_entry(const ipt_chainlabel chain, const struct ipt_entry *e, unsigned int rulenum, iptc_handle_t *handle) Description: This function replace the entry rule in chain chain positioned at rulenum with the rule defined in structure type ipt_entry. Rule numbers start at 1 for the first rule. Parameters: chain is a char pointer to the name of the chain to be modified; e is a pointer to a structure of type ipt_entry 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. rulenum 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. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.4. iptc_append_entryName: iptc_append_entry Usage: Append a new rule in a chain. Prototype: int iptc_append_entry(const ipt_chainlabel chain, const struct ipt_entry *e, iptc_handle_t *handle) Description: This function append a rule defined in structure type ipt_entry in chain chain (equivalent to insert with rulenum = length of chain). Parameters: chain is a char pointer to the name of the chain to be modified; e is a pointer to a structure of type ipt_entry 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. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.5. iptc_delete_num_entryName: iptc_delete_num_entry Usage: Delete a rule in a chain. Prototype: int iptc_delete_num_entry(const ipt_chainlabel chain, unsigned int rulenum, iptc_handle_t *handle) Description: This function delete the entry rule in chain chain positioned at rulenum. Rule numbers start at 1 for the first rule. Parameters: chain is a char pointer to the name of the chain to be modified; rulenum is an integer value defined the position in the chain of rules where the rule will be deleted. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.6. iptc_flush_entriesName: iptc_flush_entries Usage: Empty a chain. Prototype: int iptc_flush_entries(const ipt_chainlabel chain, iptc_handle_t *handle) Description: This function flushes the rule entries in the given chain (ie. empties chain). Parameters: chain is a char pointer to the name of the chain to be flushed; 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.7. iptc_zero_entriesName: iptc_zero_entries Usage: Zeroes the chain counters. Prototype: int iptc_zero_entries(const ipt_chainlabel chain, iptc_handle_t *handle) Description: This function zeroes the counters in the given chain. Parameters: chain is a char pointer to the name of the chain which counters will be zero; 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.8. iptc_create_chainName: iptc_create_chain Usage: Create a new chain. Prototype: int iptc_create_chain(const ipt_chainlabel chain, iptc_handle_t *handle) Description: This function create a new chain in the table. Parameters: chain is a char pointer to the name of the chain to be created; 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.9. iptc_delete_chainName: iptc_delete_chain Usage: Delete a chain. Prototype: int iptc_delete_chain(const ipt_chainlabel chain, iptc_handle_t *handle) Description: This function delete the chain identified by the char pointer chain in the table. Parameters: chain is a char pointer to the name of the chain to be deleted; 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.10. iptc_rename_chainName: iptc_rename_chain Usage: Rename a chain. Prototype: int iptc_rename_chain(const ipt_chainlabel oldname, const ipt_chainlabel newname, iptc_handle_t *handle) Description: This function rename the chain identified by the char pointer oldname to a new name newname in the table. Parameters: oldname is a char pointer to the name of the chain to be renamed, newname is the new name; 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.11. iptc_set_policyName: iptc_set_policy Usage: Set the policy in a built-in chain. Prototype: int iptc_set_policy(const ipt_chainlabel chain, const ipt_chainlabel policy, struct ipt_counters *counters, iptc_handle_t *handle) Description: This function set the policy in chain chain to the value represented by the char pointer policy. If you want to set at the same time the counters of the chain, fill those values in a structure of type ipt_counters and pass a pointer to it as parameter counters. Be careful: the chain must be a built-in chain. Parameters: chain is a char pointer to the name of the chain to be modified; policy is a char pointer to the name of the policy to be set. counters is a pointer to an ipt_counters structure to be used to set the counters 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 integer value 1 (true) if successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.12. iptc_zero_counterName: iptc_zero_counter Usage: Zero counters of a rule in a chain. Prototype: int iptc_zero_counter(const ipt_chainlabel chain, unsigned int rulenum, iptc_handle_t *handle) Description: This function zero packet and byte counters of the entry rule in chain chain positioned at rulenum. Rule numbers start at 1 for the first rule. Parameters: chain is a char pointer to the name of the chain to be modified; rulenum is an integer value defined the position in the chain of rules of the rule which counters will be zero. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 12.13. iptc_set_counterName: iptc_set_counter Usage: Set counters of a rule in a chain. Prototype: int iptc_set_counter(const ipt_chainlabel chain, unsigned int rulenum, struct ipt_counters *counters, iptc_handle_t *handle) Description: This function set packet and byte counters of the entry rule in chain chain positioned at rulenum with values passed in 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 modified; rulenum is an integer value defined the position in the chain of rules of the rule which counters will be set. counters is a pointer to an ipt_counters 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. 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 successful; returns integer value 0 (false) if fails. In this case errno is set to the error number generated. Use iptc_strerror to get a meaningful information about the problem. If errno == 0, it means there was a version error (ie. upgrade libiptc). 13. Bandwidth meterIn this chapter I am going to develop a simple bandwidth meter using the following functions from libiptc:
Also the function gettimeofday will be used to measure elapsed time and the function getopt to get options from the command line. I don't know really if the term bandwidth meter is well used here. I interpret bandwidth as a reference to a flow capacity; perhaps a better term could be flow meter. Here is the bandwidth meter bw.c. It's well commented to be easy followed by everyone:
Write your program and compile as before:
Before using the meter we need to set our environment. First, we have to have at least 2 PCs connected in a network. This is our diagram configuration:
Second, we need to install a very nice and useful package called netcat written by Hobbit. This excellent 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 http://rr.sans.org/audit/netcat.php. The version that I use is 1.10-277. To install it follow these instructions:
My version requires to make a patch first; check yours if you have a file with a .dif extension and apply it too:
Next compile the package using make:
Copy the binary nc to your user bin directory:
And also to the second PC in your network:
We are going to use netcat to "listen" to a flow of bytes from PC #2 and to "talk" from PC #1. Using tty1 to tty4 consoles on PC #2 let's start netcat to listen from this PC. Go to PC #2 and in tty1 type:
netcat must respond with:
This command started netcat to listen from address 192.168.1.2 using port number 1001. Arguments are: -n = use numeric address identification; -v = verbose; -l = listen. All the flow that netcat receives in 192.168.1.2:1001 will be redirected to the "black hole" in /dev/null. Repeat the command in tty2, tty3 and tty4; change to tty2 using ALT-F2 and after logging in write:
Now we are "listening" to the same address but port number 1002. Go on now with tty3:
And tty4:
Now we are listening in PC #2, address 192.168.1.2 in ports 1001, 1002, 1003 and 1004. Come back to PC #1 and let's set the environment to allow iptables to help us to complete our tests: On PC #1, type the into tty1 as follows:
These commands will:
Now start the bw meter using current values:
It must respond with:
It informs that measures are current flows. Every line is a measure taken each SLEEPTIME 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 chn_1, chn_2, chn_3 and chn_4 respectively. Of course we do not have any flow now. However let bw run and continue reading. Let's start now one of our byte flows; go to tty2 in PC #1 with ALT-F2 and after logging in, type:
netcat responds with:
Now we have a flow of bytes from PC #1 to PC #2. yes generates a constant flow of zeroes; this flow is piped to netcat through address 192.168.1.1, port 2001 and sends it to PC #2, address 192.168.1.2, port 1001 (where PC #2 is listening). Check now the display of bw in tty1:
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 chn_1 which corresponds to port number 1001 in PC #2. Let's start now the second bytes flow; go to tty3 in PC #1 with ALT-F3 and after logging in, type:
netcat responds with:
Now we have 2 flows of bytes from PC #1 to PC #2; one from 192.168.1.1:2001 to 192.168.1.2:1001 and another from 192.168.1.1:2002 to 192.168.1.2:1002. Now check the display of bw in tty1:
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. To continue, start the 2 aditional flows through channels 192.168.1.1:2003-192.168.1.2:1003 and 192.168.1.1:2004-192.168.1.2:1004. In tty4 type:
In tty5 type:
The display of bw in tty1 will be something like:
Total bandwidth is distributed between the 4 channels of flow. 14. Controlling flowsIn 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 make menuconfig, re-configure your options, re-compile and re-install your kernel. This chapter is not and does not pretend to be a tutorial about the implementation of QoS (Quality of Service) in Linux. If you don't have previous experience with QoS it's better to read some references at the end of this document to acquire the concepts required for QoS implementation. 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 libiptc) behaviour. First check if you have QoS implementation options implemented in your kernel. Run make menuconfig, follow the menu to Networking options and look for last menu of this option QoS and/or fair queueing. Here use (or check if they are active) these options:
Save your configuration, recompile your kernel and modules, and re-install it. We are going to use the CBQ packet scheduler to implement some queues to control bytes flow in our PC #1 NIC. Personally I preferred the excellent HTB queueing discipline implementation 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 CBQ happens to be. More information on HTB queueing discipline are linked at the end of this document. Having compiled and re-installed your kernel you have to install the iproute2 package that will be used to run the commands needed to implement the queues. Download this package from ftp://ftp.inr.ac.ru/ip-routing. I'm working with version 2.2.4-now-ss001007. To install it follow these instructions:
After make compiles the iproute2 package successfully the ip utility will be in iproute2/ip directory and the tc utility in iproute2/tc directory. Copy both of them to /usr/bin directory:
Now, using the tc utility, we are going to create a CBQ queue in the interface eth0 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 192.168.1.1 to 192.168.1.2 through ports 1001 to 1004. Write and run the following commands:
This command creates the main (root) cbq queue 1:0 in the eth0 interface; the bandwidth of this queue is 10Mbit/sec corresponding to our Ethernet interface. Now write and run:
This command create the main cbq class 1:1. The rate of this class will be 1000kbit/sec. 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:
Each of these classes will have a sfq queue discipline attached to them to dispatch their packets. Write and run these commands:
These commands create 4 sfq queue disciplines, one for each class. sfq queue discipline is some kind of fair controlling queue. It tries to give to each connection in an interface same oportunity to their packets to be dispatched to at all. Finally we are going to create filters to assign flows to ports 1001, 1002, 1003 and 1004 to classes 1:3, 1:4, 1:5 and 1:6 respectively. Write and run as follows:
After running all these commands, now check your bw meter (you must be running netcat listening at ports 1001 to 1004 in PC #2 and talking in PC #1 as was explained in previous chapter and bw running in current -c mode). You will have something like this:
bw show us how flows are controlling using queue disciplines of the Linux kernel. As you see, CBQ queue discipline is not a very precise queue but you more or less have a flow of approximately 1000=100+200+300+400 on interface eth0. To step back, write and run as follows:
on PC #1, to delete the main (root) queue discipline and owned classes and filters.
on PC #2 and PC #1, to stop netcat.
on PC #1, to clear iptables rules and chains.
on PC #1, tty1 to stop bw bandwidth meter. 16. About the authorLeonardo 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. He is the General Manager of OpalSoft, a venezuelan company dedicated to business packages software development. 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. His company will be opening a new area of business offering Linux QoS solution implementations in Venezuela. Married to Cielo, with 3 sons (Jose, Dario, Gustavo), he can be reached at leonardo@opalsoft.net. He is working now (please be patient) to open a QoS Linux information site at http://opalsoft.net/qos/ to interchange knowledge with people interested and to make his works in the Linux "best of all" operating system available to the public. April 30, 2002 Caracas, Venezuela |