//*****************************************************************************
//
// Program:     vip -- Programming Project 3
//
// Author:      Travis Dillon
//
// Class:       CS 444
// Email:       tdillon@ace.cs.ohiou.edu
//
// Description: This is the virtual ip program, it first finds the IP address
//              of all it's interfaces using RARP.  Then it sits and waits on
//              ARP and ICMP requests, and sends ARP and ICMP replies.
//
// Date:        January 25, 2005
//
//*****************************************************************************


#include <stdlib.h>
#include <iostream.h>
#include <vrouter.h>
#include <time.h>

#define DEFAULT_GROUP 6
#define BUFFER_SIZE 512
#define INTERFACE_NAME_LENGTH 100
#define MAX_NUM_INTERFACES 4
#define RARP_MAX_WAIT 10
#define RARP_MAX_REQUEST 10

using namespace std;
typedef unsigned char u_char;

typedef struct _Ethernet
{
   unsigned char destination[7];
   unsigned char source[7];
   unsigned char type[3];
}Ethernet;

void option_h();
void option_l(lanlist* all_lans);
void option_g(int argc, char** argv, unsigned& group, int& i);
void make_rarp_packet(const bool& debug_on, INTERFACE** ifaces, _lan* lan_ptr,
                      lanlist* all_lans);
void get_ip(const bool& debug_on, INTERFACE** ifaces, _lan* lan_ptr,
            int msec_maxwait, int num_request, lanlist* all_lans,
            char ip_array[MAX_NUM_INTERFACES][4], const int& b);
void show_ip(const bool& debug_on, const char read_buf[]);
void analyze_packet(const bool& debug_on, char buf[], const int& packet_size,
                    char ip_array[MAX_NUM_INTERFACES][4], lanlist* all_lans,
                    INTERFACE** ifaces, const size_t& c);
void make_arp_packet(const bool& debug_on, char buf[],
                     char ip_array[MAX_NUM_INTERFACES][4], const size_t& f,
                     lanlist* all_lans, INTERFACE** ifaces);
void make_icmp_packet(const bool& debug_on, char buf[], const int& packet_size,
                      char ip_array[MAX_NUM_INTERFACES][4], const size_t& f,
                      lanlist* all_lans, INTERFACE** ifaces);
void print_arp_packet(const char buf[]);
void print_icmp_echo(char buf[], const int& packet_size);

int main(int argc, char *argv[])
{
   int i, msec_maxwait(RARP_MAX_WAIT), num_request(RARP_MAX_REQUEST);
   int packet_size;
   char buf[BUFFER_SIZE], ip_array[MAX_NUM_INTERFACES][4];
   unsigned group = DEFAULT_GROUP;
   bool debug_on = false;
   lanlist* all_lans = whichlans();
   _lan* lan_ptr = all_lans -> lan;
   INTERFACE* ifaces[MAX_NUM_INTERFACES];

   if(all_lans == NULL)
   {
      cerr << "whichlans() failed and returned NULL\n";
      exit(EXIT_FAILURE);
   }

   for(i=1; i < argc; ++i)  //parse the command line
   {
      if(argv[i][0] != '-')  //at this point an argument must have a '-'
      {
         cout << "Invalid option - \""  << argv[i] << "\".\n";
         option_h();
      }
      switch(argv[i][1])
      {
         case 'd': debug_on = true; break;
         case 'l': option_l(all_lans); break;
         case 'g': option_g(argc, argv, group, i); break;
         case 'h': option_h(); break;
         default:
         {
            cout << "Invalid option - \""  << argv[i] << "\".\n";
            option_h();
            break;
         }
      }   
   }

   for(size_t j(0); j < MAX_NUM_INTERFACES; ++j)  //open all interfaces
   {
      if(lan_ptr == NULL) ifaces[j] = NULL;
      else
      {
         if(debug_on) cout << "main openinterface with j = " << j << endl;
         ifaces[j] = openinterface(lan_ptr -> lname, group);
         if(debug_on) cout << "main open " << lan_ptr -> lname << endl;
         lan_ptr = lan_ptr -> next;
      }
   }

   if(debug_on) cout << "main group = " << group << endl;

   lan_ptr = all_lans -> lan;
   for(int b(0); b < all_lans -> cnt; ++b)  //show IPs of all interfaces
   {
      if(debug_on) cout << "main get_ip() for " << lan_ptr -> lname << endl;
      get_ip(debug_on, ifaces, lan_ptr, msec_maxwait,
             num_request, all_lans, ip_array, b);
      lan_ptr = lan_ptr -> next;
   }

   //This block of code is modeled after the code Dr. Ostermann sent us in the
   //Vbridge options email.
   while(true)  //let vip run
   {
      blockforpacket(ifaces);  //wait until we have a packet somewhere
      for(size_t c(0); ifaces[c] != NULL; ++c)  //look for the packet
      {
         if(checkforpacket(ifaces[c]))  //check all interfaces for packets
         {
            packet_size = readpacket(ifaces[c], buf, BUFFER_SIZE);  //get packet
            if(debug_on) cout << "main packet_size = " << packet_size << endl;
            if(packet_size == -1)
            {
               cerr << "readpacket failed.\n";
               exit(EXIT_FAILURE);
            }
            analyze_packet(debug_on, buf, packet_size, ip_array,
                           all_lans, ifaces, c);  //process the packet
         }
      }
   }

   for(size_t j(0); ifaces[j] != NULL; ++j) closeinterface(ifaces[j]);

   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_h
//
// Purpose:    If the -h flag is given in the command line or someone enters
//             an invalid command argument, this will print out options.
//
// Parameters: none
//
// Calls:      exit
//
//****************************************************************************


void option_h()
{
   cout << "-d          To print debugging information.\n"
        << "-l          List all interfaces and exit.\n"
        << "-g NUM      Use group NUM. If NUM is ommitted then "
        << DEFAULT_GROUP << " will be used.\n"
        << "-h          Print a quick summary of the command line arguments"
        << " and exit.\n";

   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_l
//
// Purpose:    If the -l flag is given in the command line this will print
//             all the names and addresses of network devices on a computer.
//
// Parameters: all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//
// Calls:      exit
//
//****************************************************************************


void option_l(lanlist* all_lans)
{
   _lan* lan_ptr = all_lans -> lan;
   for(int j(0); j < all_lans -> cnt; ++j)
   {
      cout << "name = " << lan_ptr -> lname << endl;
      cout << "addr = " << lan_ptr -> hwaddr << endl << endl;
      lan_ptr = lan_ptr -> next;
   }
   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_g
//
// Purpose:    If the -g NUM flag is given in the command line this will
//             change the group to NUM as long as NUM is in [0, 63]
//
// Parameters: argc - number of command line arguments
//             argv - the commmand line arguments
//             i - position in the command line arguments
//
// Calls:      none
//
//****************************************************************************


void option_g(int argc, char** argv, unsigned& group, int& i)
{
   if(i < argc - 1 && argv[i + 1][0] != char(45))
   {  //true if -g isn't last command and -g has numbers after it
      group = argv[++i][0] - 48;  //ascii for zero is 48
      if(argv[i][1] != '\0')  //true if the group is greater than ten      
      {
         group = (group * 10) + (argv[i][1] - 48);
         if(argv[i][2] != '\0')  //true if the group is greater than 99
         {
            cout << "INVALID group number. Defaulting to "
                 << DEFAULT_GROUP << ".\n";
            group = DEFAULT_GROUP;
         }
      }
   }
   if(group > 63)  //valid range of group numbers is 0 - 63
   {
      cout << "INVALID group number. Defaulting to " << DEFAULT_GROUP << ".\n";
      group = DEFAULT_GROUP;
   }
}


//****************************************************************************
//
// Function:   make_rarp_packet
//
// Purpose:    Create a RARP packet to find out current interfaces IP address.
//
// Parameters: debug_on - debugging information flag
//             ifaces - handle to the interfaces
//             lan_ptr - pointer to the current interface
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//
// Calls:      writepacket, strncmp
//
//****************************************************************************


void make_rarp_packet(const bool& debug_on, INTERFACE** ifaces, _lan* lan_ptr,
                      lanlist* all_lans)
{
   char buf[BUFFER_SIZE];
   int tens, ones, hex_to_dec(1),g(6);
   _lan* temp_lan_ptr = all_lans -> lan;
   size_t t;
   
   for(size_t w(0); lan_ptr -> hwaddr[w] != '\0'; ++w)
   {  //this creates the source, next 6 octets of buffer
      tens = lan_ptr -> hwaddr[w];
      ones = lan_ptr -> hwaddr[w + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      buf[g - 6] = char(255);  //ethernet broadcast
      buf[g++] = hex_to_dec;  //my hw address is the source
      w += 2;
   }

   buf[12] = char(128);  //RARP type code is 8035 hex
   buf[13] = char(53);  //RARP type code is 8035 hex
   if(debug_on)
   {
      cout << "make_rarp_packet RARP type code = " << std::hex
      << u_char(buf[12]) * 256 + u_char(buf[13]) << std::dec << endl;   
   }
      
   //this is Hardware address space "Ethernet"
   buf[14] = char(0);
   buf[15] = char(1);  //from RFC 903 example

   //this is Protocol address space "IP"
   buf[16] = char(8);
   buf[17] = char(0);  //from RFC 903 example

   //this is byte length of each hardware address
   buf[18] = char(6);  //from lecture notes

   //this is byte length of each protocol address
   buf[19] = char(4);  //from lecture notes

   //this is opcode
   buf[20] = char(0);
   buf[21] = char(3);  //request reverse

   //this is hardware address of the sender
   buf[22] = buf[6];  //length is from buf[18]
   buf[23] = buf[7];
   buf[24] = buf[8];
   buf[25] = buf[9];
   buf[26] = buf[10];
   buf[27] = buf[11];

   //this is source protocol address, but undefined for request reverse
   buf[28] = char(0);  //length is from buf[19]
   buf[29] = char(0);
   buf[30] = char(0);
   buf[31] = char(0);

   //this will be the hardware address of the destination
   buf[32] = buf[22];  //length and data are same as buf[22 - 27]
   buf[33] = buf[23];
   buf[34] = buf[24];
   buf[35] = buf[25];
   buf[36] = buf[26];
   buf[37] = buf[27];

   //this is target protocol address, but undefined for request reverse
   buf[38] = char();  //length and data is same as buf[28 - 31]
   buf[39] = char();
   buf[40] = char();
   buf[41] = char();

   buf[42] = '\0';

   if(debug_on) cout << "make_rarp_packet writepacket() to "
                     << lan_ptr -> lname << endl;

   for(t = 0; strncmp(lan_ptr -> lname, temp_lan_ptr -> lname, 6) != 0 &&
              t <= MAX_NUM_INTERFACES; ++t) temp_lan_ptr = temp_lan_ptr -> next;
   if(t == MAX_NUM_INTERFACES)
   {
      cerr << "ERROR IN make_rarp_packet" << endl;
      exit(EXIT_FAILURE);
   }
   else writepacket(ifaces[t], buf, 42);
   if(debug_on) cout << "make_rarp_packet the RARP packet i'm sending\n";
   if(debug_on) print_arp_packet(buf);
}


//****************************************************************************
//
// Function:   get_ip
//
// Purpose:    Obtain the ip for the current interface.
//
// Parameters: debug_on - debugging information flag
//             ifaces - handle to the interfaces
//             lan_ptr - pointer to the current interface
//             msec_maxwait - how long to wait for each packet
//             num_request - how many times to check for packet
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             ip_array - array holding ip addresses of all interfaces
//             b - index into current interface
//
// Calls:      make_rarp_packet, blockforpacket_timeout, checkforpacket,
//             readpacket, show_ip
//
//****************************************************************************


void get_ip(const bool& debug_on, INTERFACE** ifaces, _lan* lan_ptr,
            int msec_maxwait, int num_request, lanlist* all_lans,
            char ip_array[MAX_NUM_INTERFACES][4], const int& b)
{   
   int packet_size;
   char read_buf[BUFFER_SIZE];

   if(debug_on) cout << "get_ip calling make_rarp_packet for "
                     << lan_ptr -> lname << endl;
   make_rarp_packet(debug_on, ifaces, lan_ptr, all_lans);
   if(debug_on) cout << "get_ip blocking for packet on "
                     << lan_ptr -> lname << endl;

   while(!blockforpacket_timeout(ifaces, msec_maxwait) && num_request > 0)
   {  //this checks 10 times for a packet over 20 seconds
      msec_maxwait *= 2;  //start at 10 milliseconds, double after each failure
      
      if(debug_on) cout << "vip: RARP response wait interval (" << msec_maxwait
                        << " ms) EXIT when " << num_request << " reaches 0!"
                        << endl;
      --num_request;  //try 10 times
   }
   if(num_request > 0)
   {
      if(debug_on) cout << "get_ip checking for packet on "
                        << lan_ptr -> lname << endl;
      if(checkforpacket(ifaces[b]))  //check current interface for packet
      {
         if(debug_on) cout << "get_ip found packet on "
                           << lan_ptr -> lname << endl;
         packet_size = readpacket(ifaces[b], read_buf, BUFFER_SIZE);
         if(debug_on)
         {
            cout << "get_ip RARP reply on " << lan_ptr -> lname << endl;
            print_arp_packet(read_buf);
         }               
         if(packet_size == -1)
         {
            cerr << "readpacket failed.\n";
            exit(EXIT_FAILURE);
         }
      }
      else
      {
         if(debug_on) cout << "get_ip no packet found on "
                           << lan_ptr -> lname << endl;
      }
   }
   else
   {
      cerr << "RARP REQUEST WAS NOT RETURNED. EXITING" << endl;
      if(debug_on) cout << "get_ip num_request = 0 no packet received" << endl;
      exit(EXIT_FAILURE);
   }
   cout << "IP address of " << lan_ptr -> lname << " is ";
   show_ip(debug_on, read_buf);
   for(size_t e(0); e < 4; ++e)
   {
      ip_array[b][e] = read_buf[e + 38];  //copy the ip to ip_array
   }

}


//****************************************************************************
//
// Function:   show_ip
//
// Purpose:    Print out the ip address
//
// Parameters: debug_on - debugging information flag
//             read_buf - the packet that was read in or created
//
// Calls:      none
//
//****************************************************************************


void show_ip(const bool& debug_on, const char read_buf[])
{
   //the last field is the ip address
   for(size_t g(38); g < 42; ++g)
   {
      cout << size_t(u_char(read_buf[g]));
      if(g != 41) cout << '.';
      else cout << endl;
   }
   //this is opcode, should be 4
   if(debug_on) cout << "show_ip " << size_t(read_buf[20])
                     << size_t(read_buf[21]) << " is the opcode" << endl;
}


//****************************************************************************
//
// Function:   analyze_packet
//
// Purpose:    Check an ip packet for arp and icmp, and process it.
//             These algorithms were given in the assignment.
//
// Parameters: debug_on - debugging information flag
//             buf - the packet that was read in or created
//             packet_size - num bytes of packet
//             ip_array - array of ip addresses for all interfaces
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             ifaces - handle to the interfaces
//             c - index of which interface we are on
//
// Calls:      make_arp_packet, print_arp_packet, make_icmp_packet,
//             print_icmp_echo
//
//****************************************************************************


void analyze_packet(const bool& debug_on, char buf[], const int& packet_size,
                    char ip_array[MAX_NUM_INTERFACES][4], lanlist* all_lans,
                    INTERFACE** ifaces, const size_t& c)
{
   size_t f;
   _lan* temp_lan_ptr = all_lans -> lan;
   int tens, ones, hex_to_dec(1);
   bool ethernet_broadcast = false, type_ip, type_rarp, type_arp, arp_request,
        my_ip = false, ethernet_match = false, icmp_echo = false;

   for(size_t d(0); d < 6; ++d)
   {
      ethernet_broadcast = (u_char(buf[d]) == u_char(255)) ? true : false;
      if(!ethernet_broadcast)
      {
         if(debug_on)
         {
            cout << "analyze_packet this is not an ETHERNET broadcast packet"
                 << endl;
         }
         break;
      }
   }
   //ip is type 800 hex
   type_ip = (buf[12] == char(8) && buf[13] == char(0)) ? true : false;
   //rarp is type 8035 hex
   type_rarp = (buf[12] == char(128) && buf[13] == char(53)) ? true : false;
   //arp is type 0806 hex
   type_arp = (buf[12] == char(8) && buf[13] == char(6)) ? true : false;
   //arp request if opcode is 3
   arp_request = (buf[20] == char(0) && buf[21] == char(3)) ? true : false;

   if(debug_on && type_arp)
   {
      cout << "analyze_packet this is the ARP packet i received" << endl;
      print_arp_packet(buf);
   }
   if(debug_on && type_ip)
   {
      cout << "analyze_packet this is the IP packet i received" << endl;
      print_icmp_echo(buf, packet_size);
   }

   for(f = 0; int(f) < all_lans -> cnt; ++f)
   {  //this check is to see if the ip address target is ''me''
      for(size_t g(0); g < 4; ++g)
      {
         my_ip = (ip_array[f][g] == buf[g + 38]) ? true : false;
         if(!my_ip) goto skip;  //try next ip in ip_array
         if(my_ip && g == 3) goto next;  //this ip matches the request
      }
      skip:;
   }
   next:;

   if(type_arp && ethernet_broadcast && my_ip)
   {  //ethernet type code is arp, ethernet destination is broadcast, and
      //the target ip is mine.
      if(debug_on) cout << "analyze_packet this ARP packet is for me" << endl;
      make_arp_packet(debug_on, buf, ip_array, c, all_lans, ifaces);
      return;
   }
   my_ip = false;

   for(size_t z(0); z < c; ++z) temp_lan_ptr = temp_lan_ptr -> next;
   for(size_t z(0); z < 6; ++z)
   {
      tens = temp_lan_ptr -> hwaddr[3*z];
      ones = temp_lan_ptr -> hwaddr[3*z + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      ethernet_match = (hex_to_dec == int(u_char(buf[z]))) ? true : false;
      if(!ethernet_match) break;
   }

   for(f = 0; int(f) < all_lans -> cnt; ++f)
   {  //this check is to see if the ip address target is ''me''
      for(size_t g(0); g < 4; ++g)
      {
         my_ip = (ip_array[f][g] == buf[g + 30]) ? true : false;
         if(!my_ip) goto skip2;  //try next ip in ip_array
         if(my_ip && g == 3) goto next2;  //this ip matches the request
      }
      skip2:;
   }
   next2:;

   icmp_echo = (buf[34] == char(8)) ? true : false;  //echo request is 8

   if(ethernet_match && type_ip && my_ip && icmp_echo)
   {
      if(debug_on) cout << "analyze_packet this is ICMP packet for me" << endl;
      make_icmp_packet(debug_on, buf, packet_size, ip_array, c,
                       all_lans, ifaces);
      return;
   }
   if(debug_on) cout << "analyze_packet this packet was not valid" << endl;
}


//****************************************************************************
//
// Function:   make_arp_packet
//
// Purpose:    Creates an ARP packet to serve as a reply to the HW addr request
//
// Parameters: debug_on - debugging information flag
//             buf - the packet that was read in or created
//             ip_array - array of ip addresses for all interfaces
//             f -
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             ifaces - handle to all interfaces
//
// Calls:      writepacket
//
//****************************************************************************


void make_arp_packet(const bool& debug_on, char buf[],
                     char ip_array[MAX_NUM_INTERFACES][4], const size_t& f,
                     lanlist* all_lans, INTERFACE** ifaces)
{
   char new_buf[43];
   _lan* lan_ptr = all_lans -> lan;
   int tens, ones, hex_to_dec(1), g(6);

   for(size_t k(0); k < f; ++k) lan_ptr = lan_ptr -> next;

   for(size_t g(0); g < 6; ++g)
   {  //destination for reply is source of request
      new_buf[g] = buf[g + 6];
   }

   for(size_t w(0); lan_ptr -> hwaddr[w] != '\0'; ++w)
   {  //my ETHERNET ADDRESS, next 6 octets of buffer
      tens = lan_ptr -> hwaddr[w];
      ones = lan_ptr -> hwaddr[w + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      new_buf[g++] = hex_to_dec;
      w += 2;
   }

   new_buf[12] = char(8);  //ARP type code is 806 hex
   new_buf[13] = char(6);  //ARP type code is 806 hex

   //Hardware address space "Ethernet"
   new_buf[14] = char(0);
   new_buf[15] = char(1);  //from RFC 903 example

   //Protocol address space "IP"
   new_buf[16] = char(8);
   new_buf[17] = char(0);  //from RFC 903 example

   //tbyte length of each hardware address
   new_buf[18] = char(6);  //from lecture notes

   //byte length of each protocol address
   new_buf[19] = char(4);  //from lecture notes

   //opcode
   new_buf[20] = char(0);
   new_buf[21] = char(2);  //reply to ARP request

   //my ETHERNET address
   new_buf[22] = new_buf[6]; 
   new_buf[23] = new_buf[7];
   new_buf[24] = new_buf[8];
   new_buf[25] = new_buf[9];
   new_buf[26] = new_buf[10];
   new_buf[27] = new_buf[11];

   //my IP address
   //get the correct ip for multi interface machines
   for(size_t z(0); z < 4; ++z) new_buf[z + 28] = ip_array[f][z];

   //hardware address of the target
   new_buf[32] = new_buf[0];
   new_buf[33] = new_buf[1];
   new_buf[34] = new_buf[2];
   new_buf[35] = new_buf[3];
   new_buf[36] = new_buf[4];
   new_buf[37] = new_buf[5];

   //this is target protocol address
   new_buf[38] = buf[28];
   new_buf[39] = buf[29];
   new_buf[40] = buf[30];
   new_buf[41] = buf[31];

   new_buf[42] = '\0';

   _lan* temp_lan_ptr = all_lans -> lan;
   for(size_t k(0); k < f; ++k) temp_lan_ptr = temp_lan_ptr -> next;

   if(debug_on) cout << "make_arp_packet writepacket() to "
                     << temp_lan_ptr -> lname << endl
                     << "make_arp_packet this is the ARP packet i'm sending"
                     << endl;
   if(debug_on) print_arp_packet(new_buf);
   writepacket(ifaces[f], new_buf, 42);
}


//****************************************************************************
//
// Function:   make_icmp_packet
//
// Purpose:    Creates an ICMP packet to serve as a reply to the ping  request
//
// Parameters: debug_on - debugging information flag
//             buf - the packet that was read in or created
//             packet_size - num of bytes of current packet
//             ip_array - array of ip addresses for all interfaces
//             f - index into ifaces of current interface
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             ifaces - handles to the interfaces
//
// Calls:      print_icmp_echo, writepacket, vchksum
//
//****************************************************************************


void make_icmp_packet(const bool& debug_on, char buf[], const int& packet_size,
                      char ip_array[MAX_NUM_INTERFACES][4], const size_t& f,
                      lanlist* all_lans, INTERFACE** ifaces)
{
   char new_buf[packet_size];
   _lan* lan_ptr = all_lans -> lan;
   int tens, ones, hex_to_dec(1), g(6);
   u_short temp_check_sum;

   for(size_t k(0); k < f; ++k) lan_ptr = lan_ptr -> next;

   //ETHERNET HEADER
   for(size_t g(0); g < 6; ++g)
   {  //destination for reply is source of request
      new_buf[g] = buf[g + 6];
   }

   for(size_t w(0); lan_ptr -> hwaddr[w] != '\0'; ++w)
   {  //my ETHERNET ADDRESS, next 6 octets of buffer
      tens = lan_ptr -> hwaddr[w];
      ones = lan_ptr -> hwaddr[w + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      new_buf[g++] = hex_to_dec;
      w += 2;
   }
   new_buf[12] = char(8);  //ICMP is IP type code is 800 hex
   new_buf[13] = char(0);  //ICMP is IP type code is 800 hex

   //IP HEADER   
   for(size_t x(14); x < 24; ++x) new_buf[x] = buf[x];  //copy beginning
   new_buf[24] = new_buf[25] = char(0);  //initialize checksum to 0x00
   for(size_t x(26); x < 30; ++x) new_buf[x] = buf[x + 4];  //swap IPs
   for(size_t x(30); x < 34; ++x) new_buf[x] = buf[x - 4];
   temp_check_sum = vchksum(&(new_buf[14]), 20);  //calculate checksum
   memcpy(&new_buf[24], &temp_check_sum, sizeof(temp_check_sum));  //update
   if(debug_on) cout << "make_icmp_packet IP vchksum = "
                     << vchksum(&(new_buf[14]),20) << endl;  //should be 0 now

   //ICMP HEADER
   new_buf[34] = char(0);  //ICMP ECHO REPLY
   new_buf[35] = char(0);  //ICMP code = 0
   new_buf[36] = char(0);  //checksum has to be 0 before it is set
   new_buf[37] = char(0);  //checksum has to be 0 before it is set
   new_buf[38] = buf[38];  //identifier stays the same
   new_buf[39] = buf[39];  //identifier stays the same
   new_buf[40] = buf[40];  //sequence number stays the same
   new_buf[41] = buf[41];  //sequence number stays the same
   //copy original data
   for(int v(42); v < packet_size; ++v) new_buf[v] = buf[v];

   //deal with the checksum
   temp_check_sum = vchksum(&(new_buf[34]), 64);  //run vchksum over entire icmp
   memcpy(&new_buf[36], &temp_check_sum, sizeof(temp_check_sum));

   if(debug_on) cout << "make_icmp_packet ICMP vchksum = "
                     << vchksum(&(new_buf[34]),64) << endl
                     << "make_icmp_packet the ICMP packet i'm sending\n";
   if(debug_on) print_icmp_echo(new_buf, packet_size);

   writepacket(ifaces[f], new_buf, packet_size);
}


//****************************************************************************
//
// Function:   print_arp_packet
//
// Purpose:    This will print out the ARP request and the ARP reply packets.
//
// Parameters: buf - the packet that was read in or created
//
// Calls:      none
//
//****************************************************************************


void print_arp_packet(const char buf[])
{
   //ETHERNET HEADER
   cout << " ETHERNET HEADER CONTENTS" << endl << "              destination  ";
   for(size_t a(0); a < 6; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 5) cout << ":";
   }
   cout << endl << "                   source  ";
   for(size_t a(6); a < 12; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 11) cout << ":";
   }
   cout << endl << "                     type  "
        << std::hex << u_char(buf[12]) * 256 + u_char(buf[13])
   //ARP FRAME CONTENTS
        << std::dec << endl << "       ARP FRAME CONTENTS" << endl
        << "   hardware address space  " << size_t(u_char(buf[14]))
        << size_t(u_char(buf[15])) << endl << "   protocol address space  "
        << size_t(u_char(buf[16])) << size_t(u_char(buf[17])) << endl
        << "    byte length of HWADDR  " << size_t(u_char(buf[18])) << endl
        << "    byte length of PRADDR  " << size_t(u_char(buf[19])) << endl
        << "                   opcode  " << size_t(u_char(buf[20]))
        << size_t(u_char(buf[21])) << endl << "          sender ethernet  ";
   for(size_t a(22); a < 28; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 27) cout << ":";
   }
   cout << endl << "                sender ip  ";
   for(size_t a(28); a < 32; ++a)
   {
      cout << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 31) cout << '.';
   }
   cout << endl << "          target ethernet  ";
   for(size_t a(32); a < 38; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 37) cout << ":";
   }
   cout << endl << "                target ip  ";
   for(size_t a(38); a < 42; ++a)
   {
      cout << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 41) cout << '.';
   }
   cout << endl;
}


//****************************************************************************
//
// Function:   print_icmp_packet
//
// Purpose:    This will print out the ICMP request and the ICMP reply packets.
//
// Parameters: buf - the packet that was read in or created
//             packet_size - size of the buffer
//
// Calls:      none
//
//****************************************************************************


void print_icmp_echo(char buf[], const int& packet_size)
{
   //ETHERNET HEADER
   cout << " ETHERNET HEADER CONTENTS" << endl << "              destination  ";
   for(size_t a(0); a < 6; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 5) cout << ":";
   }
   cout << endl << "                   source  ";
   for(size_t a(6); a < 12; ++a)
   {
      if(size_t(u_char(buf[a])) < 16) cout << '0';
      cout << std::hex << size_t(u_char(buf[a])) << std::dec;  //output in hex
      if(a != 11) cout << ":";
   }   
   cout << endl << "                     type  "
        << std::hex << u_char(buf[12]) * 256 + u_char(buf[13]) << std::dec
        << endl
   //IP HEADER
        << "       IP HEADER CONTENTS" << endl
        << "                     vers  " << size_t(u_char(buf[14])) / 16 << endl
        << "                      len  "
        << size_t(u_char(buf[14])) - (16 * (size_t(u_char(buf[14])) / 16))
        << endl << "                type serv  " << size_t(u_char(buf[15]))
        << endl << "             total length  " << size_t(u_char(buf[16]))
        << ' ' << size_t(u_char(buf[17])) << endl
        << "                    ident  " << size_t(u_char(buf[18])) << ' '
        << size_t(u_char(buf[19])) << endl << "                    flags  "
        << size_t(u_char(buf[20])) / 16 << endl << "          fragment offset  "
        << size_t(u_char(buf[20])) - (16 * (size_t(u_char(buf[20])) / 16))
        << ' ' << size_t(u_char(buf[21])) << endl
        << "                     time  " << size_t(u_char(buf[22])) << endl
        << "                    proto  " << size_t(u_char(buf[23])) << endl
        << "          header checksum  " << size_t(u_char(buf[24])) << ' '
        << size_t(u_char(buf[25])) << endl << "        source ip address  "
        << size_t(u_char(buf[26])) << '.' << size_t(u_char(buf[27])) << '.'
        << size_t(u_char(buf[28])) << '.' << size_t(u_char(buf[29])) << endl
        << "   destination ip address  " << size_t(u_char(buf[30])) << '.'
        << size_t(u_char(buf[31])) << '.' << size_t(u_char(buf[32])) << '.'
        << size_t(u_char(buf[33])) << endl
   //ICMP HEADER
        << "     ICMP HEADER CONTENTS" << endl
        << "                     type  " << size_t(u_char(buf[34])) << endl
        << "                     code  " << size_t(u_char(buf[35])) << endl
        << "                 checksum  " << size_t(u_char(buf[36])) << ' '
        << size_t(u_char(buf[37])) << endl << "               identifier  "
        << size_t(u_char(buf[38])) << ' ' << size_t(u_char(buf[39])) << endl
        << "          sequence number  " << size_t(u_char(buf[40])) << ' '
        << size_t(u_char(buf[41])) << endl << "                     data  ";
   //ICMP DATA
   for(int v(42); v < packet_size; ++v)
   {  //output the data
      if((v % 21) == 0) cout << "\n       ";  //20 bytes per line
      else
      {
         if(size_t(u_char(buf[v])) < 16) cout << '0';
         cout << std::hex << size_t(u_char(buf[v])) << std::dec << " ";
      }
   }
   cout << endl;
}