#include <ncurses.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <strings.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <signal.h>
#include "curs.h"
#include "stat.h"


void handle_frame (unsigned char buf[], int length, struct sockaddr *saddr);

main (int argc, char *argv[])
{
  int sd, op;
  struct ifreq ifr, oldifr;
  char *device = ETH;
  struct sockaddr saddr;
  int sizeaddr;
  unsigned char buf[1600];
  int length;

  if (argc == 1)
    {
      regis.e = 1;
      regis.p = 1;
      regis.t = 1;
      regis.g = 1;
    }
  else
    while ((op = getopt (argc, argv, "eptgh")) != EOF)
      switch (op)
	{
	case 'e':
	  regis.e = 1;
	  break;
	case 'p':
	  regis.p = 1;
	  break;
	case 't':
	  regis.t = 1;
	  break;
	case 'g':
	  regis.g = 1;
	  break;
	default:
	  usage (argv[0]);
	  break;
	}

  /* INIT ALARMFUCTION */

  if (signal (SIGALRM, itstime) == SIG_ERR)
    {
      perror ("Signal error: ");
      exit (5);
    }

  /* OPEN SOCKET */

  if ((sd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ALL))) < 0)
    {
      perror ("Can't get socket: ");
      exit (1);
    }

  /* SET PROMISC */

  strcpy (oldifr.ifr_name, device);
  if (ioctl (sd, SIOCGIFFLAGS, &oldifr) < 0)
    {
      close (sd);
      perror ("Can't get flags: ");
      exit (2);
    }

  ifr = oldifr;
  ifr.ifr_flags |= IFF_PROMISC;

  if (ioctl (sd, SIOCSIFFLAGS, &ifr) < 0)
    {
      close (sd);
      perror ("Can't set flags: ");
      exit (3);
    }

  /* END OF INITIALISATION */

  init_curses ();
  clrscr ();
  mvprintw (0, (COLS - 18) / 2, "NETWORK STATISTICS");
  alarm (1);

  set_null ();

  while (getch () != 'q')
    {
      sizeaddr = 1600;
      length = recvfrom (sd, buf, 1600, 0, &saddr, &sizeaddr);
      if (length == -1)
	continue;
      handle_frame (buf, length, &saddr);
    }


  /* TERMINATE */

  cleanup_curses ();

  if (ioctl (sd, SIOCSIFFLAGS, &oldifr) < 0)
    {
      close (sd);
      perror ("Can't set flags: ");
      exit (4);
    }

  close (sd);
  exit (0);
}


void
exatalk (unsigned char buf[], int length)
{
  switch (buf[34])
    {
    case 0x01:
      regis.rtmprd++;
      break;
    case 0x02:
      regis.nbp++;
      break;
    case 0x03:
      regis.atp++;
      break;
    case 0x04:
      regis.aep++;
      break;
    case 0x05:
      regis.rtmpreq++;
      break;
    case 0x06:
      regis.zip++;
      break;
    case 0x07:
      regis.adsp++;
      break;
    default:
      regis.unknown_type++;
    }
}

void
handle_other (unsigned char buf[], int length)
{
  switch (buf[20] * 256 + buf[21])
    {
    case 0x809B:
      exatalk (buf, length);
      break;
    case 0x80F3:
      regis.aarp++;
      break;
    default:
      regis.unknown_type++;
      break;
    }
}

void
handle_frame (unsigned char buf[], int length, struct sockaddr *saddr)
{
  switch (buf[12] * 256 + buf[13])
    {
    case ETH_P_IP:
      regis.ip++;
      break;
    case ETH_P_ARP:
      regis.arp++;
      break;
    case ETH_P_RARP:
      regis.rarp++;
      break;
    case ETH_P_IPX:
      regis.ipx++;
      break;
    default:
      handle_other (buf, length);
      break;
    }
  if (strncmp (saddr->sa_data, "eth", 3) == 0)
    {
      regis.etherbytes += length;
      regis.ethercount++;
    }

  else if (strncmp (saddr->sa_data, "plip", 4) == 0)
    {
      regis.plipbytes += length;
      regis.plipcount++;
    }

  else
    {
      regis.otherbytes += length;
      regis.othercount++;
    };

}

void
usage (char *arg)
{
  fprintf (stderr, "\n%s [-gepth]\n\n", arg);
  fprintf (stderr, "   Display network statistics\n");
  fprintf (stderr, "   ver 1.00 \n\n");
  fprintf (stderr, "    -g  show General window\n");
  fprintf (stderr, "    -e  show Ethernet window\n");
  fprintf (stderr, "    -p  show Plip window\n");
  fprintf (stderr, "    -t  show Types window\n");
  fprintf (stderr, "    -h  show this message\n");
  fprintf (stderr, "    no options: show all windows\n\n");
  exit (0);
}
