/*
 * Mausezahn - A fast versatile traffic generator
 * Copyright (C) 2008-2010 Herbert Haas
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License version 2 as published by the 
 * Free Software Foundation.
 * 
 * This program 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 should have received a copy of the GNU General Public License along with 
 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
 * 
*/




// ***************************************************************************
// 
//    This sections contains functions to manipulate headers of 
//    Eth, MPLS, 802.1Q, IP, UDP, and TCP:
//    
//      int rand_addr           (u_int8_t *addr)
//      int update_IP_SA        (libnet_t *l, libnet_ptag_t t)
//      int update_IP_DA        (libnet_t *l, libnet_ptag_t t)
//      int update_IP6_SA       (libnet_t *l, libnet_ptag_t t)
//      int update_IP6_DA       (libnet_t *l, libnet_ptag_t t)
//      int update_DPORT        (libnet_t *l, libnet_ptag_t t)
//      int update_SPORT        (libnet_t *l, libnet_ptag_t t)
//      int update_TCP_SQNR     (libnet_t *l, libnet_ptag_t t)
//    
//    and finally:
//    
//      int print_frame_details()
//
// ***************************************************************************

#include "mz.h"
#include "mops.h"

// Applies another random address to a given buffer.
// (The calling function should check 'tx.eth_(dst|src)_rand' whether the address
// should be randomized.)
void rand_addr(u_int8_t *addr)
{
   nrand48((unsigned short *)addr);
   nrand48((unsigned short *)(addr + 3));

   // Get rid of multicast addresses
   addr[0] &= 0xfe;

   // Set the locally administered bit
   addr[0] |= 2;
}


// Update official timestamp, own timestamp and sequence number in the RTP header. 
// The actual RTP message is stored in tx.udp_payload.
int update_RTP(libnet_t *l, libnet_ptag_t t)
{
	u_int8_t *ptr;
	struct mz_timestamp ts;
	
	tx.rtp_sqnr++;
	tx.rtp_stmp+=160; // TODO: different values for different codecs
	
	// update SQNR
	ptr = (u_int8_t*) &tx.rtp_sqnr;
	tx.udp_payload[2] = *(ptr+1);
	tx.udp_payload[3] = *ptr;

	// update official timestamp
	ptr = (u_int8_t*) &tx.rtp_stmp;
	tx.udp_payload[4] = *(ptr+3);
	tx.udp_payload[5] = *(ptr+2);
	tx.udp_payload[6] = *(ptr+1);
	tx.udp_payload[7] = *ptr;

   
	// update own timestamp
	getcurtime(&ts); // Now add TX timestamp:
	mops_hton4 ((u_int32_t*) &ts.sec,  &tx.udp_payload[16]);
	mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]);
   
	t = libnet_build_udp(tx.sp, 
			     tx.dp, 
			     tx.udp_len, 
			     tx.udp_sum,
			     tx.udp_payload,
			     tx.udp_payload_s,
			     l, 
			     t);

	if (t == -1) {
		fprintf(stderr," mz/send_frame: RTP header update failed!\n");
		exit (1);
	}
	return 0;
}


///////////////////////////////////////////////////////////////////////////
// Applies another SOURCE IP address, 
//  - either a random one (tx.ip_src_rand==1)
//  - or from a specified range (tx.ip_src_isrange==1) 
// to a given IP-PTAG.
// 
// Note: tx.ip_src MUST be already initialized with tx.ip_src_start.
//       This is done by 'get_ip_range_src()' in tools.c.
// 
// 
// RETURNS '1' if tx.ip_src restarts
// 
int update_IP_SA (libnet_t *l, libnet_ptag_t t)
{
   u_int8_t *x, *y;  
   int i=0;
   
   if (ipv6_mode) {
	   return update_IP6_SA(l, t);
   }

   if (tx.ip_src_rand)
     {
	tx.ip_src_h  = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0
	i=1;
     }
   else if (tx.ip_src_isrange)
     {
	tx.ip_src_h++;
	if (tx.ip_src_h > tx.ip_src_stop) // reached the end of the range => restart!
	  {
	     tx.ip_src_h = tx.ip_src_start;
	     i=1;
	  }
     }
   
   // Now convert "tx.ip_src_h" into "tx.ip_src" which is in 'Network Byte Order':
   x = (unsigned char*) &tx.ip_src_h;
   y = (unsigned char*) &tx.ip_src;
   
   *y = *(x+3);
   y++;
   *y = *(x+2);
   y++;
   *y = *(x+1);
   y++;
   *y = *x;
   
   // TODO: Omit certain IP addresses:
   //       E.g. if (rand_ip == tx.ip_src) goto rand_again;  // never use true interface IP
   // TODO: Check other address exceptions ...

   t = libnet_build_ipv4 (tx.ip_len, 
			  tx.ip_tos, 
			  tx.ip_id, 
			  tx.ip_frag,
			  tx.ip_ttl, 
			  tx.ip_proto,
			  tx.ip_sum, 
			  tx.ip_src,             // possibly now random
			  tx.ip_dst,
			  (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,  // if e.g. mode=UDP ignore payload argument
			  (mode==IP) ? tx.ip_payload_s : 0,
			  l, 
			  t);

   if (t == -1)
     {
	fprintf(stderr," mz/update_IP_SA: IP address manipulation failed!\n");
	exit (1);
     }

   return i;
}


int
update_IP6_SA(libnet_t *l, libnet_ptag_t t)
{
	int i = 0;
	if (tx.ip_src_rand) {
		fprintf(stderr, "Random source addresses are not supported in IPv6 mode.\n");
		exit(1);
	} else if (tx.ip_src_isrange) {
		if (incr_in6_addr(tx.ip6_src, &tx.ip6_src)
		     || (in6_addr_cmp(tx.ip6_src, tx.ip6_src_stop) > 0))
		{
			tx.ip6_src = tx.ip6_src_start;
			i = 1;
		}
	}

	t = libnet_build_ipv6(tx.ip_tos,
			      tx.ip_flow,
			      tx.ip_len,
			      tx.ip_proto,
			      tx.ip_ttl,
			      tx.ip6_src,
			      tx.ip6_dst,
			      (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
			      (mode==IP) ? tx.ip_payload_s : 0,
			      l,
			      t);

	if (t == -1) {
		fprintf(stderr," mz/update_IP6_SA: IP address manipulation failed!\n");
		exit (1);
	}

	return i;
}


/////////////////////////////////////////////////////////////////////////////////////////
// Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) 
// to a given IP-PTAG.
// 
// Note: tx.ip_dst MUST be already initialized with tx.ip_dst_start.
//       tx.ip_dst_h 'mirrors' tx.ip_dst 
//       (i. e. tx.ip_dst_h is NOT in network byte order => easy to count)
//       This is done by 'get_ip_range_dst()' in tools.c.
// 
// RETURN VALUE: '1' if tx.ip_dst restarts
// 
int update_IP_DA(libnet_t *l, libnet_ptag_t t)
{
   u_int8_t *x, *y;  
   int i=0;

   if (ipv6_mode) {
	   return update_IP6_DA(l, t);
   }

   if (tx.ip_dst_isrange)
     {
	tx.ip_dst_h++;
	if (tx.ip_dst_h > tx.ip_dst_stop) // we reached the end of the range => restart!
	  {
	     tx.ip_dst_h = tx.ip_dst_start;
	     i=1;
	  }
     }

   
   // Now convert "tx.ip_dst_h" into "tx.ip_dst" which is in 'Network Byte Order':

   x = (unsigned char*) &tx.ip_dst_h;
   y = (unsigned char*) &tx.ip_dst;
   
   *y = *(x+3);
   y++;
   *y = *(x+2);
   y++;
   *y = *(x+1);
   y++;
   *y = *x;

   
   // TODO: Omit certain IP addresses:
   //       E.g. if (rand_ip == tx.ip_src) goto rand_again;  // never use true interface IP
   // TODO: Check other address exceptions ...
   
   t = libnet_build_ipv4 (tx.ip_len, 
			  tx.ip_tos, 
			  tx.ip_id, 
			  tx.ip_frag,
			  tx.ip_ttl, 
			  tx.ip_proto,
			  tx.ip_sum, 
			  tx.ip_src, 
			  tx.ip_dst,
			  (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,  // if e.g. mode=UDP ignore payload argument
			  (mode==IP) ? tx.ip_payload_s : 0,
			  l, 
			  t);

   if (t == -1)
     {
	fprintf(stderr," mz/update_IP_DA: IP address manipulation failed!\n");
	exit (1);
     }

   return i;
}


int
update_IP6_DA(libnet_t *l, libnet_ptag_t t)
{
	int i = 0;
	if (tx.ip_dst_isrange) {
		if (incr_in6_addr(tx.ip6_dst, &tx.ip6_dst)
		     || (in6_addr_cmp(tx.ip6_dst, tx.ip6_dst_stop) > 0))
		{
			tx.ip6_dst = tx.ip6_dst_start;
			i = 1;
		}
	}

	t = libnet_build_ipv6(tx.ip_tos,
			      tx.ip_flow,
			      tx.ip_len,
			      tx.ip_proto,
			      tx.ip_ttl,
			      tx.ip6_src,
			      tx.ip6_dst,
			      (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
			      (mode==IP) ? tx.ip_payload_s : 0,
			      l,
			      t);

	if (t == -1) {
		fprintf(stderr," mz/update_IP6_DA: IP address manipulation failed!\n");
		exit (1);
	}

	return i;
}



///////////////////////////////////////////////////////////////////////////////////////
//
// Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG.
// 
// Note: tx.dp MUST be already initialized with tx.dp_start
//       This is done by 'get_port_range()' in tools.c.
//
// RETURN VALUE: '1' if tx.dp restarts
//      
int update_DPORT(libnet_t *l, libnet_ptag_t t)
{
  // u_int32_t DP;
   int i=0;
   
  // DP = (u_int32_t) tx.dp;
  // DP++;
   tx.dp++;

   
   // Exceeded range => restart:
   if ((tx.dp > tx.dp_stop) ||  // we exceeded the end of the range 
       (tx.dp == 65535) )       // or exceeded the 16-bit range
     {
	tx.dp = tx.dp_start;
	i=1;
     }
   
   
   if (mode==UDP)
     {
	t = libnet_build_udp(tx.sp, 
			     tx.dp, 
			     tx.udp_len, 
			     tx.udp_sum,
			     (tx.udp_payload_s) ? tx.udp_payload : NULL,
			     tx.udp_payload_s, 
			     l, 
			     t);

	if (t == -1)
	  {
	     fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n");
	     exit (1);
	  }
     }
   else // TCP
     {
	t = libnet_build_tcp (tx.sp,
			      tx.dp,
			      tx.tcp_seq, 
			      tx.tcp_ack,
			      tx.tcp_control,
			      tx.tcp_win, 
			      tx.tcp_sum, 
			      tx.tcp_urg, 
			      tx.tcp_len,
			      (tx.tcp_payload_s) ? tx.tcp_payload : NULL,
			      tx.tcp_payload_s, 
			      l, 
			      t);
		  
	if (t == -1)
	  {
	     fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l));
	     exit (0);
	  }  
     }
   
   return i;
}

   
///////////////////////////////////////////////////////////////////////////////////
//
// Applies another SOURCE PORT from a specified range to a given UDP- or TCP-PTAG.
// 
// Note: tx.sp MUST be already initialized with tx.sp_start
//       This is done by 'get_port_range()' in tools.c.
//       
// RETURN VALUE: '1' if tx.sp restarts
//       
int update_SPORT(libnet_t *l, libnet_ptag_t t)
{
   
//   u_int32_t SP;
   int i=0;
   
//   SP = (u_int32_t) tx.sp;
//   SP++;
   tx.sp++;

   
   // Exceeded range => restart:
   if ((tx.sp > tx.sp_stop) ||  // we exceeded the end of the range 
       (tx.sp == 65535) )       // or exceeded the 16-bit range
     {
	tx.sp = tx.sp_start;
	i=1;
     }
     
   if (mode==UDP)
     {
	t = libnet_build_udp(tx.sp,
			     tx.dp, 
			     tx.udp_len, 
			     tx.udp_sum,
			     (tx.udp_payload_s) ? tx.udp_payload : NULL,
			     tx.udp_payload_s, 
			     l, 
			     t);

	if (t == -1)
	  {
	     fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n");
	     exit (1);
	  }
     }
   else // TCP
     {
	t = libnet_build_tcp (tx.sp,
			      tx.dp, 
			      tx.tcp_seq, 
			      tx.tcp_ack,
			      tx.tcp_control,
			      tx.tcp_win, 
			      tx.tcp_sum, 
			      tx.tcp_urg, 
			      tx.tcp_len,
			      (tx.tcp_payload_s) ? tx.tcp_payload : NULL,
			      tx.tcp_payload_s, 
			      l, 
			      t);
		  
	if (t == -1)
	  {
	     fprintf(stderr, " mz/update_SPORT: Can't build TCP header: %s\n", libnet_geterror(l));
	     exit (0);
	  }  
     }
   
   return i;   
}

#define LIBNET_CKSUM_CARRY(x) \
    (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))

int update_USUM(libnet_t *l, libnet_ptag_t t)
{
     int sum = 0;
     unsigned int tmp;

     if (tx.udp_sum != 0)
	return 0;

     sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16);
     if (tx.ip_option_s && tx.ip6_segs)
       sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address
     else
       sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16);

     tmp = htonl(tx.udp_len);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);
     tmp = htonl(IPPROTO_UDP);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = ((htons(tx.sp) << 16) + htons(tx.dp));
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = htons(tx.udp_len) << 16;
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     if (tx.udp_payload_s)
       sum += libnet_in_cksum((u_int16_t *) tx.udp_payload, tx.udp_payload_s);

     tx.udp_sum = ntohs(LIBNET_CKSUM_CARRY(sum));

     t = libnet_build_udp(tx.sp,
			  tx.dp,
			  tx.udp_len,
			  tx.udp_sum,
			  tx.udp_payload_s ? tx.udp_payload : NULL,
			  tx.udp_payload_s,
			  l,
			  t);
     tx.udp_sum = 0;
     return t;
}

int update_TSUM(libnet_t *l, libnet_ptag_t t)
{
     int sum = 0;
     unsigned int tmp;

     if (tx.tcp_sum != 0)
	return 0;

     sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16);
     if (tx.ip_option_s && tx.ip6_segs)
       sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address
     else
       sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16);

     tmp = htonl(tx.tcp_len);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);
     tmp = htonl(IPPROTO_TCP);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = ((htons(tx.sp) << 16) + htons(tx.dp));
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = htonl(tx.tcp_seq);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);
     tmp = htonl(tx.tcp_ack);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = ((ntohs(((tx.tcp_offset) << 12) + tx.tcp_control) << 16) + htons(tx.tcp_win));
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = htonl(tx.tcp_urg);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     sum += tx.tcp_sum_part;

     if (tx.tcp_payload_s)
       sum += libnet_in_cksum((u_int16_t *) tx.tcp_payload, tx.tcp_payload_s);

     tx.tcp_sum = ntohs(LIBNET_CKSUM_CARRY(sum));

     t = libnet_build_tcp (tx.sp,
	                   tx.dp,
	                   tx.tcp_seq,
	                   tx.tcp_ack,
	                   tx.tcp_control,
	                   tx.tcp_win,
	                   tx.tcp_sum,
	                   tx.tcp_urg,
	                   tx.tcp_len,
	                   tx.tcp_payload_s ? tx.tcp_payload : NULL,
	                   tx.tcp_payload_s,
	                   l,
	                   t);
     tx.tcp_sum = 0;

   return t;
}

int update_ISUM(libnet_t *l, libnet_ptag_t t)
{
     int sum = 0;
     unsigned int tmp;

     if (tx.icmp_chksum != 0)
	return 0;

     sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16);
     if (tx.ip_option_s && tx.ip6_segs)
       sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address
     else
       sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16);

     tmp = htonl(LIBNET_ICMPV6_H + tx.icmp_payload_s);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);
     tmp = htonl(IPPROTO_ICMP6);
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     tmp = htonl(((tx.icmp_type << 8) + tx.icmp_code));
     sum += libnet_in_cksum((u_int16_t *) &tmp, 4);

     if (tx.icmp_payload_s)
       sum += libnet_in_cksum((u_int16_t *) tx.icmp_payload, tx.icmp_payload_s);

     tx.icmp_chksum = ntohs(LIBNET_CKSUM_CARRY(sum));

     t = libnet_build_icmpv4_echo (tx.icmp_type,
				   tx.icmp_code,
				   tx.icmp_chksum,
				   tx.icmp_ident,
				   tx.icmp_sqnr,
				   tx.icmp_payload_s ? tx.icmp_payload : NULL,
				   tx.icmp_payload_s,
				   l,
				   t);
     tx.icmp_chksum = 0;

   return t;
}

///////////////////////////////////////////////////////////////////////
//
// Applies another TCP SQNR from a specified range to a given TCP-PTAG
// 
// RETURN VALUE: '1' if tx.txp_seq restarts
// 
int update_TCP_SQNR(libnet_t *l, libnet_ptag_t t)
{

   u_int32_t diff;
   int i=0;
   
   tx.tcp_seq += tx.tcp_seq_delta;
   diff = tx.tcp_seq_stop - tx.tcp_seq_start;

   if (diff < tx.tcp_seq_stop) // start < stop
     {
	if (tx.tcp_seq > tx.tcp_seq_stop)
	  {
	     tx.tcp_seq = tx.tcp_seq_start;
	     i=1;
	  }
     }
   else // stop < start
     {
	if ( (tx.tcp_seq<tx.tcp_seq_start) && 
	     (tx.tcp_seq>tx.tcp_seq_stop) )
	  {
	     tx.tcp_seq = tx.tcp_seq_start;
	     i=1;
	  }
	
     }
   
   t = libnet_build_tcp (tx.sp,
			 tx.dp, 
			 tx.tcp_seq, 
			 tx.tcp_ack,
			 tx.tcp_control,
			 tx.tcp_win, 
			 tx.tcp_sum, 
			 tx.tcp_urg, 
			 tx.tcp_len,
			 (tx.tcp_payload_s) ? tx.tcp_payload : NULL,
			 tx.tcp_payload_s, 
			 l, 
			 t);
   
   if (t == -1)
     {
	fprintf(stderr, " mz/update_TCP_SQNR: Can't build TCP header: %s\n", libnet_geterror(l));
	exit (0);
     }  
   
   return i;
}


////////////////////////////////////////////////////////////////////////
//
//

int print_frame_details(void)
{
   unsigned char *dum1, *dum2;
   char pld[65535];
   char sa[32], da[32];
   
   if (!tx.packet_mode)
     {
	bs2str(tx.eth_dst, da, 6);
	bs2str(tx.eth_src, sa, 6);
	fprintf(stderr, " Eth: DA = %s, SA = %s\n",da,sa);
     }
   
   
   if (tx.dot1Q)
     {
	fprintf(stderr, " 802.1Q VLAN-TAG = %s\n", tx.dot1Q_txt);
     }

   if (tx.mpls)
     {
	fprintf(stderr," MPLS labels (label:exp:bos:ttl): %s\n",tx.mpls_verbose_string);
	
     }

   
   dum1 =  (unsigned char*) &tx.ip_src_h;
   dum2 = (unsigned char*) &tx.ip_dst_h;
   if (mode==IP) {
     (void) bs2str(tx.ip_payload, pld, tx.ip_payload_s);
   } else {
     strcpy(pld, "[see next layer]");
   }

   if (ipv6_mode) {
     char src6[64]; char dst6[64];
     libnet_addr2name6_r(tx.ip6_src, LIBNET_DONT_RESOLVE, src6, 64);
     libnet_addr2name6_r(tx.ip6_dst, LIBNET_DONT_RESOLVE, dst6, 64);

     fprintf(stderr," IP:  ver=6, dscp=%u, flow=%u, len=%u, next=%u, hop=%u "
             "SA=%s, DA=%s\n      payload=%s\n", tx.ip_tos, tx.ip_flow,
	     tx.ip_len, tx.ip_proto, tx.ip_ttl, src6, dst6, pld);
   }
   else {
     fprintf(stderr," IP:  ver=4, len=%u, tos=%u, id=%u, frag=%u, ttl=%u, proto=%u, sum=%u, "
	     "SA=%u.%u.%u.%u, DA=%u.%u.%u.%u,\n"
	     "      payload=%s\n", tx.ip_len, tx.ip_tos,
	     tx.ip_id, tx.ip_frag, tx.ip_ttl, tx.ip_proto, tx.ip_sum,
	     *(dum1+3),*(dum1+2),*(dum1+1),*(dum1), *(dum2+3),*(dum2+2),*(dum2+1),*(dum2+0), pld);
   }
   
   if ((mode==UDP)||(mode==DNS)||(mode==RTP))
     {
	bs2str(tx.udp_payload, pld, tx.udp_payload_s);
	fprintf(stderr, " UDP: sp=%u, dp=%u, len=%u, sum=%u, \n"
		"      payload=%s\n", tx.sp, tx.dp, tx.udp_len, tx.udp_sum, pld);
     }
   if (mode==TCP) // TODO: Improve message details (flags, ...)
     {
	bs2str(tx.tcp_payload, pld, tx.tcp_payload_s);
	fprintf(stderr, " TCP: sp=%u, dp=%u, S=%u, A=%u, flags=%x, win=%u, len=%u, sum=%u, \n"
		"      payload=%s\n", 
		tx.sp, tx.dp, tx.tcp_seq, tx.tcp_ack, tx.tcp_control, tx.tcp_win, tx.tcp_len, tx.tcp_sum, pld);
     }
   
   // send_icmp must prepare the verbose string because there are many
   // different types of ICMP packets...
   if (mode==ICMP) 
     {
	fprintf(stderr, " %s\n", tx.icmp_verbose_txt);
     }

   if (mode==ICMP6)
     {
	fprintf(stderr, " %s\n", tx.icmp_verbose_txt);
     }
   
   // libnet_diag_dump_pblock(l);
   fprintf(stderr,"\n");

   if (simulate)
     {
	fprintf(stderr, "*** NOTE: Simulation only! Nothing has been sent! ***\n");
	exit(0);
     }
   
   
   return 0;
}

