Personal tools

Difference between revisions of "Libipq by example"

From linux360

Jump to: navigation, search
m
(Fixed wiki syntax (language type for highlighting); constrained code header to 80 columns (beautify pass 1))
Line 1: Line 1:
Am adaugat aici codul sursa al unui proiect facut cu libipq, cu scopul de a servi ca exemplu celor interesati. La inceputul codului sunt si instructiuni de compilare si de rulare + testare. Codul este destul de amplu comentat, asa ca ar trebui sa fie usor de urmarit, daca aveti in fata paginile de manual ale comenzilor din api-ul libipq. Comentariile sunt scrise in engleza pentru a fi de ajutor mai multor persoane...
+
Am adaugat aici codul sursa al unui proiect facut cu <tt>libipq</tt>, cu scopul de a servi drept exemplu celor interesati. La inceputul codului sunt instructiuni de compilare si de rulare + testare. Codul este destul de amplu comentat, asa ca ar trebui sa fie usor de urmarit daca aveti in fata si paginile de manual ale comenzilor din api-ul libipq. Comentariile sunt scrise in engleza pentru a fi de ajutor mai multor persoane ...
  
 
Multa bafta!
 
Multa bafta!
  
<code type="c">
+
<code><c/>/* netsim.c  29 Mar 2006
/* netsim.c  29 Mar 2006
 
 
    
 
    
 
   This code is released under GPL
 
   This code is released under GPL
 
   You can modify it and do anything with it, as long as it is released under GPL.
 
   You can modify it and do anything with it, as long as it is released under GPL.
 
    
 
    
   A note for people interested in learning how to use libipq: Read this mini documentation before trying to understand the code!
+
   A note for people interested in learning how to use libipq: Read this mini  
   * To understand the code, make sure you have the man page of libipq nearby and start reading from the beginning of the  
+
  documentation before trying to understand the code!
      main() function; This is because not all functions in this file is relevant to libipq. Follow the comments and you should be fine.
+
   * To understand the code, make sure you have the man page of libipq nearby and  
      This project doesn't use features higher than layer 3 (IP), so I won't look into the TCP/UDP header (although it's possible).  
+
    start reading from the beginning of the main() function; This is because not  
      Check out this link to see another example that does this: http://www.crhc.uiuc.edu/~grier/projects/libipq.html
+
    all functions in this file is relevant to libipq. Follow the comments and you  
 +
    should be fine.
 +
  * This project doesn't use features higher than layer 3 (IP), so I won't look  
 +
    into the TCP/UDP header (although it's possible).  
 +
 
 +
  Check out this link to see another example that does this:  
 +
  http://www.crhc.uiuc.edu/~grier/projects/libipq.html
 
        
 
        
      Here is a copy of the man page: http://www.cs.princeton.edu/~nakao/libipq.htm
+
  Here is a copy of the man page: http://www.cs.princeton.edu/~nakao/libipq.htm
 
    
 
    
 
   Authors: Dumitrascu Irina (dumitrascu.irina@gmail.com)
 
   Authors: Dumitrascu Irina (dumitrascu.irina@gmail.com)
Line 22: Line 27:
 
   Version: 0.1
 
   Version: 0.1
 
    
 
    
   Purpose: A network simulator (delays, errors, duplicates, reorders and drops packets that are sent to it)
+
   Purpose: A network simulator (delays, errors, duplicates, reorders and drops  
 +
            packets that are sent to it)
 
    
 
    
 
   How to compile: gcc -o netsim netsim.c -lipq  (requires libipq-dev)
 
   How to compile: gcc -o netsim netsim.c -lipq  (requires libipq-dev)
Line 30: Line 36:
 
        
 
        
 
       |Host A|----------------------|Router|------------------------|Host B|
 
       |Host A|----------------------|Router|------------------------|Host B|
                .2                    .1          .1                    .2
+
          .2                    .1          .1                    .2
 
                   10.0.0.0/24                  192.168.0.0/24
 
                   10.0.0.0/24                  192.168.0.0/24
 
                    
 
                    
       First, set an iptables rule that will send certain packets to IPQ (you need iptable_filter and ip_queue modules loaded):  
+
       First, set an iptables rule that will send certain packets to IPQ (you  
 +
      need iptable_filter and ip_queue modules loaded):  
 
              
 
              
            Router# echo "1" >/proc/sys/net/ipv4/ip_forward      # start routing
+
        Router# echo "1" >/proc/sys/net/ipv4/ip_forward       
            Router# iptables -A FORWARD -p icmp --icmp-type echo-request -j QUEUE  # send all icmp echo requests to IPQ
+
                # start routing
 +
        Router# iptables -A FORWARD -p icmp --icmp-type echo-request -j QUEUE   
 +
                # send all icmp echo requests to IPQ
 
              
 
              
 
       Start netsim with the option(s) you want:
 
       Start netsim with the option(s) you want:
 
              
 
              
            Router#./netsim --delay min=0,max=10,percent=30
+
        Router#./netsim --delay min=0,max=10,percent=30
 
              
 
              
 
       Start a ping from Host A:  
 
       Start a ping from Host A:  
 
              
 
              
            HostA# ping 192.168.0.2
+
        HostA# ping 192.168.0.2
 
        
 
        
       (if you get a Network Unreachable error, make sure that you have a default gw or route information about 192.168 set)
+
       (if you get a Network Unreachable error, make sure that you have a default  
 +
      gateway or route information about 192.168 set)
 
       You should see the results on the ping output and on the Router's screen.
 
       You should see the results on the ping output and on the Router's screen.
 
    
 
    
Line 58: Line 68:
 
             Router# ./netsim --drop percent=22
 
             Router# ./netsim --drop percent=22
 
             Failed to send netlink message: Connection refused
 
             Failed to send netlink message: Connection refused
       Answer: you need to load the ip_queue module (modprobe ip_queue) or set a iptables -j QUEUE rule
+
       Answer: you need to load the ip_queue module (modprobe ip_queue) or set an
 +
              iptables -j QUEUE rule
 
        
 
        
 
       Command:
 
       Command:
 
             Router# ./netsim --drop percent=22
 
             Router# ./netsim --drop percent=22
 
             Failed to create netlink socket
 
             Failed to create netlink socket
       Answer: you can't run two instances of netsim at the same time on the same machine. Kill one of them.
+
       Answer: you can't run two instances of netsim at the same time on the same  
 +
              machine. Kill one of them.
 
        
 
        
 
   Known bugs/errors/limitations:
 
   Known bugs/errors/limitations:
       - can't create new packets (libipq limitation), so the duplication is done by copying the content of packet i to packet i+1 (and
+
       - can't create new packets (libipq limitation), so the duplication is done by  
         loosing the original content of packet i+1)
+
        copying the content of packet i to packet i+1 (and losing the original
       - if there are multiple iptables -j QUEUE rules active, it's possible to have an unpredictible output for --reorder and --duplicate
+
         content of packet i+1)
        (can be fixed if you check to see that all packets that are to be reordered or duplicated come from the same iptables rule)
+
       - if there are multiple iptables -j QUEUE rules active, it's possible to have  
       - if you modify a packet and want it to go through, you MUST recalculate its CRC sum. Otherwise it MIGHT be dropped by the
+
        an unpredictible output for --reorder and --duplicate (can be fixed if you  
        kernel, and will surely be dropped at the destination.  
+
        check to see that all packets that are to be reordered or duplicated come  
       - the percentages set in the command line are not always respected; they are respected only for a very large number of packets.
+
        from the same iptables rule)
       - in the case of excessive delays set by the delay parameter, you might experience packet drops because the packet queue can  
+
       - if you modify a packet and want it to go through, you MUST recalculate its  
        fill up.
+
        CRC sum. Otherwise it MIGHT be dropped by the kernel, and will surely be  
       - by default, iptables drops all packets that match a -j QUEUE rule if no ipq listener is registered (netsim isn't started)
+
        dropped at the destination.  
 +
       - the percentages set in the command line are not always respected; they are  
 +
        respected only for a very large number of packets.
 +
       - in the case of excessive delays set by the delay parameter, you might  
 +
        experience packet drops because the packet queue can fill up.
 +
       - by default, iptables drops all packets that match a -j QUEUE rule if no ipq  
 +
        listener is registered (netsim isn't started)
 
          
 
          
   Disclaimer: This program can be DANGEROUS, so make sure that you know what you are doing before using it on a network. It  
+
   Disclaimer: This program can be DANGEROUS, so make sure that you know what you  
      was intended only as an exercise; it shouldn't be used in real life! You were warned... (If you notice network problems after you
+
              are doing before using it on a network. It was intended only as an  
      start netsim, kill it and issue a 'iptables -F' to delete all the rules)
+
              exercise; it shouldn't be used in real life! You were warned...  
 +
              (If you notice network problems after you start netsim, kill it and  
 +
              issue an 'iptables -F' to delete all the rules)
 
*/
 
*/
  
#include <linux/netfilter.h>  //constants  
+
#include <linux/netfilter.h>  // constants  
#include <linux/ip.h>           //ip header
+
#include <linux/ip.h>         // ip header
#include <libipq/libipq.h>    //libipq API
+
#include <libipq/libipq.h>    // libipq API
#include <stdio.h>             //general definitions
+
#include <stdio.h>           // general definitions
#include <stdlib.h>             //for exit()
+
#include <stdlib.h>           // for exit()
#include <string.h>             //for command line parsing
+
#include <string.h>           // for command line parsing
#include <time.h>               //for srand()
+
#include <time.h>             // for srand()
#include <unistd.h>           //for usleep()
+
#include <unistd.h>           // for usleep()
#include <signal.h>           //for signal() (ctrl+c)
+
#include <signal.h>           // for signal() (ctrl+c)
  
 
#define BUFFERSIZE 2048
 
#define BUFFERSIZE 2048
Line 702: Line 722:
 
   return 0;
 
   return 0;
 
    
 
    
}
+
}</code>
 
 
 
 
</code>
 
  
  
 
[[Category:Networking]]
 
[[Category:Networking]]

Revision as of 23:04, 31 March 2006

Am adaugat aici codul sursa al unui proiect facut cu libipq, cu scopul de a servi drept exemplu celor interesati. La inceputul codului sunt instructiuni de compilare si de rulare + testare. Codul este destul de amplu comentat, asa ca ar trebui sa fie usor de urmarit daca aveti in fata si paginile de manual ale comenzilor din api-ul libipq. Comentariile sunt scrise in engleza pentru a fi de ajutor mai multor persoane ...

Multa bafta!

<c/>/* netsim.c 29 Mar 2006

  This code is released under GPL
  You can modify it and do anything with it, as long as it is released under GPL.
  
  A note for people interested in learning how to use libipq: Read this mini 
  documentation before trying to understand the code!
  * To understand the code, make sure you have the man page of libipq nearby and 
    start reading from the beginning of the main() function; This is because not 
    all functions in this file is relevant to libipq. Follow the comments and you 
    should be fine.
  * This project doesn't use features higher than layer 3 (IP), so I won't look 
    into the TCP/UDP header (although it's possible). 
  Check out this link to see another example that does this: 
  http://www.crhc.uiuc.edu/~grier/projects/libipq.html
     
  Here is a copy of the man page: http://www.cs.princeton.edu/~nakao/libipq.htm
  
  Authors: Dumitrascu Irina (dumitrascu.irina@gmail.com)
                Adrian Popa (adrian.popa.gh@gmail.com)
  
  Version: 0.1
  
  Purpose: A network simulator (delays, errors, duplicates, reorders and drops 
           packets that are sent to it)
  
  How to compile: gcc -o netsim netsim.c -lipq  (requires libipq-dev)
  
  How to run: 
     -consider the following network topology:
     
     |Host A|----------------------|Router|------------------------|Host B|
          .2                    .1          .1                     .2
                 10.0.0.0/24                  192.168.0.0/24
                 
     First, set an iptables rule that will send certain packets to IPQ (you 
     need iptable_filter and ip_queue modules loaded): 
           
       Router# echo "1" >/proc/sys/net/ipv4/ip_forward      
               # start routing
       Router# iptables -A FORWARD -p icmp --icmp-type echo-request -j QUEUE  
               # send all icmp echo requests to IPQ
           
     Start netsim with the option(s) you want:
           
       Router#./netsim --delay min=0,max=10,percent=30
           
     Start a ping from Host A: 
           
       HostA# ping 192.168.0.2
     
     (if you get a Network Unreachable error, make sure that you have a default 
     gateway or route information about 192.168 set)
     You should see the results on the ping output and on the Router's screen.
  
  Common errors/troubleshooting:
     Command:
           Router$ ./netsim --drop percent=22
           Received an error message: 1
     Answer: netsim must run with root privileges
     
     Command:
           Router# ./netsim --drop percent=22
           Failed to send netlink message: Connection refused
     Answer: you need to load the ip_queue module (modprobe ip_queue) or set an
             iptables -j QUEUE rule
     
     Command:
           Router# ./netsim --drop percent=22
           Failed to create netlink socket
     Answer: you can't run two instances of netsim at the same time on the same 
             machine. Kill one of them.
     
  Known bugs/errors/limitations:
     - can't create new packets (libipq limitation), so the duplication is done by 
       copying the content of packet i to packet i+1 (and losing the original 
       content of packet i+1)
     - if there are multiple iptables -j QUEUE rules active, it's possible to have 
       an unpredictible output for --reorder and --duplicate (can be fixed if you 
       check to see that all packets that are to be reordered or duplicated come 
       from the same iptables rule)
     - if you modify a packet and want it to go through, you MUST recalculate its 
       CRC sum. Otherwise it MIGHT be dropped by the kernel, and will surely be 
       dropped at the destination. 
     - the percentages set in the command line are not always respected; they are 
       respected only for a very large number of packets.
     - in the case of excessive delays set by the delay parameter, you might 
       experience packet drops because the packet queue can fill up.
     - by default, iptables drops all packets that match a -j QUEUE rule if no ipq 
       listener is registered (netsim isn't started)
       
  Disclaimer: This program can be DANGEROUS, so make sure that you know what you 
              are doing before using it on a network. It was intended only as an 
              exercise; it shouldn't be used in real life! You were warned... 
              (If you notice network problems after you start netsim, kill it and 
              issue an 'iptables -F' to delete all the rules)
  • /
  1. include <linux/netfilter.h> // constants
  2. include <linux/ip.h> // ip header
  3. include <libipq/libipq.h> // libipq API
  4. include <stdio.h> // general definitions
  5. include <stdlib.h> // for exit()
  6. include <string.h> // for command line parsing
  7. include <time.h> // for srand()
  8. include <unistd.h> // for usleep()
  9. include <signal.h> // for signal() (ctrl+c)
  1. define BUFFERSIZE 2048

void showUsage(); int parseCmdline(int argc, char **argv); void showCmdline(); int validateCmdline(); void exitWithError(struct ipq_handle *h);

/* A structure holding different variables set based on the command line parameters.

*/

struct {

  unsigned short int delay;
  unsigned int delay_min;
  unsigned int delay_max;
  unsigned short int delay_percent;
  unsigned short int drop;
  unsigned short int drop_percent;
  unsigned short int reorder;
  unsigned short int reorder_percent;
  unsigned int reorder_max;
  unsigned short int duplicate;
  unsigned short int duplicate_percent;
  unsigned short int error;
  unsigned short int error_percent;
  unsigned short int mangle;
  unsigned short int mangle_percent;

} cmdline;

/*Global variables - for statistics*/ int total_packets=0, \

     delayed_packets=0, \
     dropped_packets=0, \
     duplicated_packets=0, \
     errored_packets=0, \
     reordered_packets=0, \
     mangled_packets=0;

/* Method to parse the command line -> takes argc and argv from main and writes

* flags in the structure cmdline. (This is NOT the most efficient way to do this, but hey...)
* Returns 0 for success and anything else for failiure.
*/

int parseCmdline(int argc, char **argv){

  int i=0;
  for ( i = 1; i < argc; i++)
   {
      if(strstr(argv[i],"--delay")!=NULL){  //we found the delay parameter
         cmdline.delay=1; //set to do delay
         //the next parameters should contain min,max and percent
         if((i+1)==argc) return 1; //no more arguments
         char* min=strstr(argv[i+1], "min");
         char* max=strstr(argv[i+1], "max");
         char* percent=strstr(argv[i+1], "percent");
         if(min!=NULL && max!=NULL && percent!=NULL){
            //everything is as expected
            char* temp=min;    //get the first parameter
            temp = strchr(temp, '='); //extract from '=' to the end
            temp = strtok(temp, ","); //cut the first comma
            temp = temp +1; //cut the equal from the result
            //fprintf(stderr, "DEBUG: temp=%s\n ", temp);
            cmdline.delay_min=atoi(temp);
            //fprintf(stderr, "DEBUG: cmdline.delay_min=%d\n ", cmdline.delay_min);
            //now, for the second parameter
            temp = max;
            temp = strchr(temp, '='); //extract from '=' to the end
            temp = strtok(temp, ","); //cut the first comma
            temp = temp +1; //cut the equal from the result
            //fprintf(stderr, "DEBUG: temp=%s\n ", temp);
            cmdline.delay_max=atoi(temp);
            //fprintf(stderr, "DEBUG: cmdline.delay_max=%d\n ", cmdline.delay_max);
            //the last parameter
            temp = percent;
            temp = strchr(temp, '='); //extract from '=' to the end
            temp = temp +1; //cut the equal from the result
            //fprintf(stderr, "DEBUG: temp=%s\n ", temp);
            cmdline.delay_percent=atoi(temp);
            //fprintf(stderr, "DEBUG: cmdline.delay_percent=%d\n ", cmdline.delay_percent);
            i++; //skip a parameter, because it has been parsed
         }//from all the NULL's
         else{
            return 1;
         }
         continue;  //start over
      }//from --delay
      
      if(strstr(argv[i],"--drop")!=NULL){  //we found the --drop parameter
         cmdline.drop=1; //set to do drop
         //fprintf(stderr, "DEBUG: i=%d, argc=%d\n ", i, argc);
         if((i+1)==argc) return 1; //no more parameters
         char* percent=strstr(argv[i+1], "percent");
         if(percent!=NULL){
            percent=strchr(percent, '='); //extract from '=' to the end
            percent=percent+1; //cut the equal from the result
            cmdline.drop_percent=atoi(percent);
            //fprintf(stderr, "DEBUG: cmdline.drop_percent=%d\n ", cmdline.drop_percent);
            i++; //skip a parameter because it has been parsed
         }//from if
         else{
            //showUsage();
            return 1;
         }
         continue;
      }//from --drop
      
      if(strstr(argv[i],"--duplicate")!=NULL){  //we found --duplicate
         cmdline.duplicate=1; //set to do duplicate
         if((i+1)==argc) return 1; //no more parameters
         char* percent=strstr(argv[i+1], "percent");
         if(percent!=NULL){
            percent=strchr(percent, '='); //extract from '=' to the end
            percent=percent+1; //cut the equal from the result
            cmdline.duplicate_percent=atoi(percent);
            i++; //skip a parameter because it has been parsed already
         }//from if
         else{
            //showUsage();
            return 1;
         }
         continue;
      }//from --duplicate
      
      if(strstr(argv[i],"--error")!=NULL){  //we found --error
         cmdline.error=1; //set to do some error (as if internal bugs weren't enough! :)
         if((i+1)==argc) return 1; //no more parameters
         char* percent=strstr(argv[i+1], "percent");
         if(percent!=NULL){
            percent=strchr(percent, '='); //extract from '=' to the end
            percent=percent+1; //cut the equal from the result
            cmdline.error_percent=atoi(percent);
            i++; //skip a parameter because it has been parsed
         }//from if
         else{
            //showUsage();
            return 1;
         }
         continue;
      }//from --error
      
      if(strstr(argv[i],"--mangle")!=NULL){  //we found mangle
         cmdline.mangle=1; //set to do mangle
         if((i+1)==argc) return 1; //no more parameters
         char* percent=strstr(argv[i+1], "percent");
         if(percent!=NULL){
            percent=strchr(percent, '='); //extract from '=' to the end
            percent=percent+1; //cut the equal from the result
            cmdline.mangle_percent=atoi(percent);
            i++; //skip a parameter because it has been parsed already
         }//from if
         else{
            //showUsage();
            return 1;
         }
         continue;
      }//from --mangle
      
      if(strstr(argv[i],"--reorder")!=NULL){  //we found reorder
         cmdline.reorder=1; //set to do reordering
         if((i+1)==argc) return 1; //no more parameters
         char* percent=strstr(argv[i+1], "percent");
         char* max=strstr(argv[i+1], "max");
         if(percent!=NULL && max!=NULL){
            char *temp = percent;
            temp=strchr(temp, '='); //extract from '=' to the end of the string
            temp=strtok(temp, ","); //cut what's after the comma
            temp=temp+1; //cut the equal from the result
            cmdline.reorder_percent=atoi(temp);
            temp=max;
            temp=strchr(temp, '='); //extract from '=' to the end
            temp=temp+1; //cut the equal from the result
            cmdline.reorder_max=atoi(temp);
            i++; //skip a parameter because it has been parsed
         }//from if
         else{
            //showUsage();
            return 1;
         }
         continue;
      }//from --reorder
   }
   return 0;

}


/* Debug method, used to test if parameter parsing works as expected.

*/

void showCmdline(){

  fprintf(stderr, "\nDEBUG: cmdline.delay=%d",cmdline.delay);
  fprintf(stderr, "\nDEBUG: cmdline.delay_min=%d",cmdline.delay_min);
  fprintf(stderr, "\nDEBUG: cmdline.delay_max=%d",cmdline.delay_max);
  fprintf(stderr, "\nDEBUG: cmdline.delay_percent=%d",cmdline.delay_percent);
  fprintf(stderr, "\nDEBUG: cmdline.drop=%d",cmdline.drop);
  fprintf(stderr, "\nDEBUG: cmdline.drop_percent=%d",cmdline.drop_percent);
  fprintf(stderr, "\nDEBUG: cmdline.reorder=%d",cmdline.reorder);
  fprintf(stderr, "\nDEBUG: cmdline.reorder_percent=%d",cmdline.reorder_percent);
  fprintf(stderr, "\nDEBUG: cmdline.reorder_max=%d",cmdline.reorder_max);
  fprintf(stderr, "\nDEBUG: cmdline.duplicate=%d",cmdline.duplicate);
  fprintf(stderr, "\nDEBUG: cmdline.duplicate_percent=%d",cmdline.duplicate_percent);
  fprintf(stderr, "\nDEBUG: cmdline.error=%d",cmdline.error);
  fprintf(stderr, "\nDEBUG: cmdline.error_percent=%d",cmdline.error_percent);
  fprintf(stderr, "\nDEBUG: cmdline.mangle=%d",cmdline.mangle);
  fprintf(stderr, "\nDEBUG: cmdline.mangle_percent=%d",cmdline.mangle_percent);

}


/* Method that checks that the values in the structure cmdline are relevant

* (ex: percent <100)
* Returns 0 for success and anything else for failiure.
*/

int validateCmdline(){

  if( (cmdline.delay_min>cmdline.delay_max) || (cmdline.reorder_max>5) || (cmdline.delay_percent>100) \
     || (cmdline.drop_percent>100) || (cmdline.reorder_percent>100) || (cmdline.duplicate_percent>100) \
     || (cmdline.error_percent>100) || (cmdline.mangle_percent>100) || (cmdline.reorder_max<=0))
        return 1;
  else
  return 0;

}

/* Method that displays the usage information (help)

*/

void showUsage(){

  fprintf(stderr, "\nnetsim v0.1\n\nUsage: netsim OPTION...");
  fprintf(stderr, "\nOption list:");
  fprintf(stderr, "\n  --delay\t\t\tdelays some packets");
  fprintf(stderr, "\n      min=TIME\t\t\tminimum delay time in ms");
  fprintf(stderr, "\n      max=TIME\t\t\tmaximum delay time in ms");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be delayed");
  fprintf(stderr, "\n  --drop\t\t\tdrops some packets");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be dropped");
  fprintf(stderr, "\n  --duplicate\t\t\tduplicates some packets");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be duplicated");
  fprintf(stderr, "\n  --error\t\t\tcreates bit errors in some packets");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be errored");
  fprintf(stderr, "\n  --mangle\t\t\tmodifies TOS in ip header");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be mangled");
  fprintf(stderr, "\n  --reorder\t\t\tmodifies order of packets");
  fprintf(stderr, "\n      max=NUMBER\t\tnumber of packets to reorder");
  fprintf(stderr, "\n      percent=PERCENT\t\tpercent of packets to be duplicated");
  fprintf(stderr, "\n\nExamples:");
  fprintf(stderr, "\nnetsim --delay min=12,max=20,percent=5");
  fprintf(stderr, "\nnetsim --drop percent=3");
  fprintf(stderr, "\nnetsim --duplicate percent=3");
  fprintf(stderr, "\nnetsim --delay min=0,max=12,percent=7 --error percent=3 --duplicate percent=2\n");

}


/* Method used to exit cleanly in case of errors in libipq

*/

void exitWithError(struct ipq_handle *h){

  ipq_perror("Queue Error:");
  ipq_destroy_handle(h);
  exit(2);

}


/* Dumps the packet in hex on the screen - used for debug

*/

void showPacket(ipq_packet_msg_t *pkt){

  int i=0;
  printf("\n Raw packet: --------------------------\n");
  for(i=0;i<pkt->data_len;i++){
     printf("%x ", pkt->payload[i]);
  }
  printf("\n End of raw packet ------------------");

}

/*Checksum for ip header*/ unsigned short checksum(unsigned short *addr, int len) {

 int nleft=len;
 int sum=0;
 unsigned short *w=addr;
 unsigned short answer=0;

while(nleft>1){

     sum+=*w++;
     nleft-=2;

} if(nleft==1){

     *(unsigned char *)(&answer)=*(unsigned char *)w;
     sum+=answer;     

}

 sum=(sum>>16)+(sum&0xffff);
 sum+=(sum>>16);
 answer=~sum;
 return answer;

}


/*Method that calculates the percent*/ int doPercent(int percent){

  if (percent>0)
     return rand()%((int)(100/percent+0.5));
  else
     return 1;

}

/*Handler for CTRL+C; Displays some statistics when exiting*/ void ctrlC(int sig){

  printf("\nStatistics:");
  printf("\nTotal packets=%d Delayed=%d Dropped=%d\nDuplicated=%d Errored=%d Reordered=%d Mangled=%d\n", \
        total_packets,delayed_packets, dropped_packets, duplicated_packets, errored_packets, reordered_packets, \
        mangled_packets);
  exit(0);

}


/* ---Verdict methods--- */ /* Can return 0 if the packet is to be accepted without modification,

*                   1 if the packet is to be accepted with modification(s),
*                   2 if the packet is to be dropped.

/* Method that decides whether a packet should be errored */ /* Returns 0 if the packet is to be accepted without modification,

*              1 if the packet is to be accepted with modification(s),
*/

int doError(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.error_percent)==0){ //if we need to do error
     printf(" erroring ");
     struct iphdr *iph = ((struct iphdr *)pkt->payload);  //get the ip header
     iph->tot_len=iph->tot_len+1;   //modify the ip packet length field so that the destination receives a bad CRC
     errored_packets++;
     return 1;  //send the modified packet (to be forwarded)
  }
 return 0; //nothing to do to this packet

} /* Method that decides whether a packet should be delayed */ /* Returns 0 if the packet is to be accepted without modification,

*/

int doDelay(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.delay_percent)==0){ //if we need to delay the packet
     printf(" delaying ");
     int ms = rand()%(cmdline.delay_max - cmdline.delay_min); //calculate this delay (randomly)
     ms+=cmdline.delay_min;
     delayed_packets++;
     usleep(ms*1000); //sleeping 'ms' miliseconds
  }
  
  return 0; //forward the packet normally

} /* Method that decides whether a packet should be dropped */ /* Returns 0 if the packet is to be accepted without modification,

*              2 if the packet is to be dropped.
*/

int doDrop(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.drop_percent)==0){//if we need to drop this packet
     printf(" dropping ");
     dropped_packets++;
     return 2; //notify that it is to be dropped
     }
  return 0; //forward normally

} /* Method that decides whether a packet should be reordered */ /* Returns 0 if the packet is to be accepted without modification,

*              1 if the packet is to be accepted with modification(s),
*/

int doReorder(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.reorder_percent)==0){ //if this packet needs to be reordered
     printf(" reordering ");
     
     /*here it gets tricky. To reorder, one must receive all packets and send them again in the reverse order.
       This is just what we do. Note that your packets will be delayed (because we're waiting for cmdline.reorder_max
       packets to arrive first)*/
     
     ipq_packet_msg_t *packets[]={ NULL, NULL, NULL, NULL, NULL } ; //an array to hold 5 packets (maximum).
     unsigned char buffer[5][BUFFERSIZE];  //local buffer
     int status=0, i=0;
     
     packets[0]=pkt; //copy the current (previously obtained) packet to packets[0]; This will be the last to leave
     reordered_packets++;
     printf("."); //display a '.' for every packet sent from now on
     for(i=1; i<cmdline.reorder_max;i++){
        //read the next packet
        status = ipq_read(h, buffer[i], BUFFERSIZE, 0);  //handle h, destination_buffer, buffer_size, timeout
        if (status<0)
           exitWithError(h);
        
        //determine the message type -> Packet or error message
        if(ipq_message_type(buffer[i])==NLMSG_ERROR){
           fprintf(stderr, "Received an error message in doReorder(): %d", ipq_get_msgerr(buffer[i]));  //display the error.
        }
        if(ipq_message_type(buffer[i])==IPQM_PACKET){
           //we received an actual packet of data.
           pkt=ipq_get_packet(buffer[i]); //write the actual packet in pkt   
           total_packets++;
           reordered_packets++;
           packets[i]=pkt; //store this packet into the array, where it will be read later
           printf(".");
        }
     }
     
     //do the reordering and set verdicts
     
     for(i=cmdline.reorder_max-1; i>0; i--){ //go backwards
        pkt=packets[i];
        status=ipq_set_verdict(h, pkt->packet_id, NF_ACCEPT, pkt->data_len, pkt->payload); //set the verdict for each 
                                                                                                                 //packet, until we get to the last one
        //fprintf(stderr, "*");
     }
     
     pkt=packets[0]; //let the main program set the verdict for the first packet.
     return 1; //forward the modified packet (it was copied from packets[0])
  }
  return 0; //no reordering, nothing special to do

} /* Method that decides whether a packet should be duplicated */ /* Returns 0 if the packet is to be accepted without modification,

*              1 if the packet is to be accepted with modification(s),
*/

int doDuplicate(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.duplicate_percent)==0){//if we must duplicate this packet
     printf(" duplicating ");
     
     int status = 0;
     //showPacket(pkt);
     
     /*There's a catch about outputting new packets using libipq. Normally, libipq can't create new packets (that weren't
       captured by the iptables filter. So, in order to do duplication, we have to discard the next packet that arrives and 
       overwrite its content with the content of the original packet. It works, but may hide some bugs (not tested extensivelly)*/
     
     //save this packet somewhere;
     ipq_packet_msg_t *new_pkt=pkt;
     //allow the original packet to pass (first instance)
     status=ipq_set_verdict(h, pkt->packet_id, NF_ACCEPT, 0, NULL); 
     //wait for a new packet.
     //read an IPQ message into buffer.
     unsigned char buffer[BUFFERSIZE];
     status = ipq_read(h, buffer, BUFFERSIZE, 0);  //handle h, destination_buffer, buffer_size, timeout
     if (status<0)
        exitWithError(h);
     
     //determine the message type -> Packet or error message
     if(ipq_message_type(buffer)==NLMSG_ERROR){
        fprintf(stderr, "Received an error message in doDuplicate(): %d", ipq_get_msgerr(buffer));  //display the error.
     }
     if(ipq_message_type(buffer)==IPQM_PACKET){
        //we received an actual packet of data.
        total_packets++;
        duplicated_packets++;
        //replace its contents with the previous packet (so that this will be the duplicated one)
        pkt=ipq_get_packet(buffer); //write the actual packet in pkt
        long id = pkt->packet_id; //ipq isn't stupid! It knows it has forwarded the packet with the old id, so we must keep 
                                                  //the new packets id (they are unique) (the old id isn't valid after we set the verdict)
        pkt = new_pkt;  //overwrite the second packet with the first packet
        pkt->packet_id = id; //use the second (valid) id
     }
     return 1; //send the second, modified packet
  }
  return 0; //nothing special to do

} /* Method that decides whether a packet should be mangled */ /* Returns 0 if the packet is to be accepted without modification,

*              1 if the packet is to be accepted with modification(s),
*/

int doMangle(struct ipq_handle *h, ipq_packet_msg_t *pkt){

  if(doPercent(cmdline.mangle_percent)==0){//mangle this packet?
  printf(" mangling ");
  mangled_packets++;
  struct iphdr *iph = ((struct iphdr *)pkt->payload); //get the ip header
  iph->tos=(__u8)24;  //modify the TOS field (can be any field) to a specific value
  iph->check=0; //set the checksum to 0
  iph->check=checksum((unsigned short*)iph,iph->ihl*4); //compute the new checksum
  return 1;//forward the modified packet
  }
  return 0;

} /* ---End of verdict methods--- /*

/* Main method

*/

int main(int argc, char **argv){

  if(argc<3){ //not enough arguments, here's a list
     showUsage();
     exit(1);
  }
  
  //register a listener for CTRL+C, so that when we stop, we get a nice statistic
  (void) signal(SIGINT, ctrlC);
  
  memset( &cmdline, 0, sizeof(cmdline)); //initialize cmdline with zeros;
  cmdline.reorder_max=1;   //can't be zero, or it won't be valid
  //showCmdline(); //Debug
  
  
  /*If you want to understand how libipq can be used, you shouldn't bother looking over parseCmdline() 
  because it is irrelevant to your goal; it just parses the command line options and populates the cmdline 
  variable.*/
  if(parseCmdline(argc, argv)!=0){      //write the data to cmdline
     showUsage();
     exit(1);
  }
  //showCmdline(); //Debug
  
  if(validateCmdline()!=0){   //check that cmdline has reasonable values
     showUsage();
     //fprintf(stderr, "DEBUG: Invalid parameter");
     exit(1);
  }
  /*showCmdline(); //Debug
  exit(1);
  */
  
  //initialize random number generator (needed for --delay)
  srand( (unsigned int)time( NULL ) );
  unsigned char buffer[BUFFERSIZE];  //buffer for packages and ipq messages.
  struct ipq_handle *h;
  int status;
  
  //Register a handle with IPQ.
  h = ipq_create_handle(0, PF_INET);  //0=Flags (not used); PF_INET -> IPV4 
  if (!h)
     exitWithError(h);
  
  //Copy entire packets (up to BUFFERSIZE) to user-space
  status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFFERSIZE);
  if (status <0)
     exitWithError(h);
  
  //Get the packets and act on them according to the cmdline parameters
  //Do this in an infinite loop.
  do{
     //read an IPQ message into buffer. The IPQ message isn't the packet!!!
     status = ipq_read(h, buffer, BUFFERSIZE, 0);  //handle h, destination_buffer, buffer_size, timeout
     if (status<0)
        exitWithError(h);
     
     //determine the message type -> Packet or error message
     if(ipq_message_type(buffer)==NLMSG_ERROR){
        fprintf(stderr, "Received an error message: %d", ipq_get_msgerr(buffer));  //display the error.
     }
     if(ipq_message_type(buffer)==IPQM_PACKET){
        //we received an actual packet of data.
        ipq_packet_msg_t *pkt=ipq_get_packet(buffer); //write the actual packet in pkt 
        
        total_packets++;
        
        //all we have to do now is set the verdict!
        int verdict = 0; //allow all by default.
        int verdictDrop = 0, verdictDelay = 0, verdictError = 0, verdictReorder = 0, verdictDuplicate = 0, verdictMangle = 0;
        
        printf("\n Packet ");
        if(cmdline.drop==1){ //if we need to drop a percent of  packets
           verdictDrop = doDrop(h, pkt);
           //printf(" drop ");
        }
        if(verdictDrop !=2 && cmdline.error ==1){ //if it was dropped, don't do anything else to the packet
           verdictError = doError(h, pkt);
           //printf(" error ");
        }
        if(verdictDrop !=2 && verdictError !=1 && cmdline.delay ==1){ //if it was dropped or errored, don't do anything else to it
           verdictDelay = doDelay(h, pkt);
           //printf( " delay ");
        }
        if(verdictDrop !=2 && verdictError !=1 && cmdline.mangle ==1){
           verdictMangle = doMangle(h, pkt);
           //printf(" mangle ");
        }
        if(verdictDrop !=2 && verdictError !=1 && cmdline.reorder ==1){
           verdictReorder = doReorder(h, pkt);
           //printf(" reorder ");
        }
        if(verdictDrop !=2 && verdictError !=1 && cmdline.duplicate ==1){
           verdictDuplicate = doDuplicate(h, pkt);
           //printf(" duplicate ");
        }
        
        fflush(stdin); //make sure the output is updated
        
        if(verdictDrop ==2) //we really need to drop this packet
           verdict = 2;
        else{ //we need to send a modified packet
           if(verdictError ==1 || verdictMangle ==1 || verdictReorder ==1 || verdictDuplicate ==1)
              verdict = 1;
           else
              verdict = 0; //we will send the original packet
        }
        
        //we have the verdict, now enforce it!
        
        if(verdict == 0){
           //accept packet without modifications
           status=ipq_set_verdict(h, pkt->packet_id, NF_ACCEPT, 0, NULL); 
                                               // handle, packet id, verdict, data_size, buffer to output.
        }
        if(verdict == 1){
           //accept packet with modifications
           status=ipq_set_verdict(h, pkt->packet_id, NF_ACCEPT, pkt->data_len, pkt->payload); 
                                               // handle, packet id, verdict, data_size, buffer to output.
        }
        if(verdict == 2){
           //drop packet
           status=ipq_set_verdict(h, pkt->packet_id, NF_DROP, 0, NULL);
                                               // handle, packet id, verdict, data_size, buffer to output.
        }
        
        if(status < 0)
           exitWithError(h);
        
        /*printf("\nPacket ok, size=%d", pkt->data_len);
        showPacket(pkt);
        */
        
     }
     
  } while(1);
  
  //unreachable code.
  ipq_destroy_handle(h);
  return 0;
  

}