/**************************************************************************** 
** File: datalink.c
**
** Author: Mike Borella
**
** Comments: Generic datalink module
**
** $Id: datalink.c,v 1.26 2007/06/25 10:44:49 farooq-i-azam Exp $
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This 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 Library General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "global.h"
#include "error.h"
#include "datalink.h"
#include "ethernet.h"
#include "loopback.h"
#include "slip.h"
#include "raw.h"
#include "ppp.h"
#include "padding.h"
#include "payload.h"
#include "stats.h"
#include "dynports.h"

extern struct arg_t * my_args;
extern int            packet_displayed;
extern int            start_of_packet;

/* Map for datalink types */
strmap_t datalink_type_map[] = 
  {
    { DATALINK_TYPE_ETHERNET,  "ethernet" },
    { DATALINK_TYPE_8023,      "802.3" },
    { DATALINK_TYPE_TOKENBUS,  "token bus" },
    { DATALINK_TYPE_TOKENRING, "token ring" },
    { DATALINK_TYPE_METRONET,  "metro net" },
    { DATALINK_TYPE_HDLC,      "HDLC" },
    { DATALINK_TYPE_CHARSYNCH, "character synchronous" },
    { DATALINK_TYPE_IBMC2C,    "IBM channel-to-channel" },
    { DATALINK_TYPE_FDDI,      "FDDI" },
    { DATALINK_TYPE_NULL,      "null" },
    { DATALINK_TYPE_SLIP,      "SLIP" },
    { DATALINK_TYPE_PPP,       "PPP" },
    { DATALINK_TYPE_RAWIP,     "raw ip" },
    { 0, ""}
  };

/*----------------------------------------------------------------------------
**
** datalink_pcap()
**
** Libpcap specific wrapper for the generic datalink function
** 
**----------------------------------------------------------------------------
*/

void datalink_pcap(u_char * user, const struct pcap_pkthdr * h, u_char * pkt)
{
  int            linktype = -1;
  u_int32_t *    link;
  struct timeval tv;

  /* Translate the link type */
  link = (u_int32_t *) user;
  switch(*link)
    {
    case DLT_NULL:
      linktype = DATALINK_TYPE_NULL;
      break;
      
    case DLT_EN10MB:
      linktype = DATALINK_TYPE_ETHERNET; 
      break;
      
    case DLT_SLIP:
      linktype = DATALINK_TYPE_SLIP;
      break;
      
    case DLT_PPP:
      linktype = DATALINK_TYPE_PPP;
      break;
      
#ifdef DLT_RAW /* Not supported in some arch or older pcap versions */
    case DLT_RAW:
      linktype = DATALINK_TYPE_RAWIP;
      break;
#endif
    default:
      error_fatal("\ncannot handle data link type %d", link);
    }

  /* 
   * Copy the timestamp our own timeval struct to avoid compilation 
   * errors with older versions of libpcap.
   */
  
  tv.tv_sec = h->ts.tv_sec;
  tv.tv_usec = h->ts.tv_usec;

  /* Call the generic datalink function */
  datalink(linktype, tv, h->caplen, h->len, pkt);
}

/*----------------------------------------------------------------------------
**
** datalink()
**
**----------------------------------------------------------------------------
*/

void datalink(int linktype, struct timeval ts, u_int32_t captured_length, 
	      u_int32_t media_length, u_int8_t * pkt)
{
  packet_t    packet;
  static int  count = 1; /* count of the number of packets */

  /*
   * For minimal mode.  We haven't displayed part of a packet yet...
   * Set the start of packet flag so that we can display a nicer separator
   */

  packet_displayed = 0;
  start_of_packet = 1;

  /*
   * Reset the stats counted flag and unpause the counting if paused.
   * This avoids the double-counting of encapsulated headers.
   */

  stats_reset();
  stats_unpause();

  /*
   * Set up packet into data structure
   */

  packet.contents = my_malloc(captured_length);
  memcpy(packet.contents, pkt, captured_length);
  packet.current = &packet.contents[0];
  packet.end = &packet.contents[0] + captured_length;
  packet.apparent_end = packet.end;
  packet.media_length = media_length;

  /*
   * Print time stamp is human readable format.
   */

  snprintf(packet.timestamp, PACKET_TIMESTAMP_LEN, "%s", 
	   ctime ( ( const time_t * ) &ts.tv_sec ) );

  /*
   * If we're in minimal mode, start off with the count number
   * and the optional packet timestamp
   */
  
  if (my_args->m)
    {
      display_minimal((u_int8_t *) &count, 4, DISP_DEC);
      display_minimal_string(" ");
      count ++;

      if (!my_args->T)
	{
	  display_minimal_string(packet.timestamp);
	  display_minimal_string(" ");
	}
    }
  
  /* 
   * Delete any remaining hex buffer contents
   */
  
  hexbuffer_kill();
  
  /*
   * Determine what to do next
   */
  
  switch(linktype)
    {
    case DATALINK_TYPE_NULL:
      dump_loopback(&packet);
      break;
      
    case DATALINK_TYPE_ETHERNET:
      dump_ethernet(&packet);
      break;
      
    case DATALINK_TYPE_SLIP:
      dump_slip(&packet);
      break;
      
    case DATALINK_TYPE_PPP:
      dump_ppp(&packet);
      break;
      
    case DATALINK_TYPE_RAWIP:
      dump_raw(&packet);
      break;

    default:
      error_fatal("\ncannot handle data link type %d", linktype);
    }

  /*
   * Make sure we set the layer back to a null value in case one of 
   * the layers was suppressed.
   */

  set_layer(LAYER_NONE);

  /* 
   * Both payload and padding shouldn't be displayed in minimal mode 
   */
  if (!my_args->m)
    {
      /*
       * If there is any remaining data and the user wants to see it, dump it 
       * out now
       */
      
      if (my_args->p && get_packet_apparentbytesleft(&packet))
	dump_payload(&packet);
      
      /* 
       * If there is a padding and the user wants to see it, dump it out now
       */
      
      if (my_args->d && packet_haspadding(&packet))
	dump_padding(&packet);
    }

  /*
   * For minimal mode, finish with a carriage return
   */
  
  if (my_args->m && packet_displayed)
    {
      display_minimal_cr();
      display_minimal_cr();
    }

  /*
   * Time out stale dynamic port mappings
   */
  
  dynports_timeout();

  /*
   * Deallocate memory
   */
  
  my_free(packet.contents);
      
}
