/*
 *
 * Original Author: Luke Stras <stras@ecf.toronto.edu>
 *
 * Revision History
 * 1999/09/22 stras Initial version
 * 1999/09/25 JHS   re-use for PPPOE discovery 
 * 1999/09/26 JHS   retransmission for PADI and PADR
 * 1999/10/11 ireid@sympatico.ca   -I fix
 * 1999/10/22  AC cookie echo, -F, -R, -P and help extensions
 * 1999/10/23  small fix by Josef Drexler to create_padr(); 
 *		doesnt make sense and doesnt work if removed 
 *		(will figure it out later). Looks like alignm issue
 *		Fixed: Stupid mistake on my part (much cleaner now).
 *
 *
 */

#include <getopt.h>
#include "pppoed.h"
/*
#define DISC_DEB 1
#define DISC_DEB2 1
*/

/* Bigger than the biggest ethernet packet we'll ever see, in bytes */
#define MAX_PACKET	2000

int ctrl_sock;
int opt_verbose = 0;


/* 
   pointer to packet we have to retransmit
 */

struct pppoe_packet *ret_pkt;
int ret_pkt_size = 0;
FILE *log_file = NULL;
int state = CODE_PADI;

void
print_hex (unsigned char *buf, int len)
{
  int i;

  if (opt_verbose == 0)
    return;

  for (i = 0; i < len; i++)
    printf ("%02x ", (unsigned char) *(buf + i));

  printf ("\n");
}


void
print_packet (struct pppoe_packet *p)
{
  int i;
  struct pppoe_tag *t = (struct pppoe_tag *) (p + 1);
  char *buf;

  printf ("\nEthernet header:\n");
  printf ("h_dest: ");
  for (i = 0; i < 6; i++)
    printf ("%02x:", (unsigned) p->ethhdr.h_dest[i]);

  printf ("\nh_source: ");
  for (i = 0; i < 6; i++)
    printf ("%02x:", (unsigned) p->ethhdr.h_source[i]);

  printf ("\nh_proto: 0x%04x ", (unsigned) ntohs (p->ethhdr.h_proto));
  switch ((unsigned) ntohs (p->ethhdr.h_proto)) {
  case ETH_P_PPPOE_DISC:
    printf ("(PPPOE Discovery)\n");
    break;
  case ETH_P_PPPOE_SESS:
    printf ("(PPPOE Session)\n");
    break;
  default:
    printf ("(Unknown)\n");
  }

  printf ("PPPoE header: \nver: 0x%01x type: 0x%01x code: 0x%02x "
	  "session: 0x%04x length: 0x%04x ", (unsigned) p->ver,
	  (unsigned) p->type, (unsigned) p->code, (unsigned) p->session,
	  (unsigned) ntohs (p->length));

  switch (p->code) {
  case CODE_PADI:
    printf ("(PADI)\n");
    break;
  case CODE_PADO:
    printf ("(PADO)\n");
    break;
  case CODE_PADR:
    printf ("(PADR)\n");
    break;
  case CODE_PADS:
    printf ("(PADS)\n");
    break;
  case CODE_PADT:
    printf ("(PADT)\n");
    break;
  default:
    printf ("(Unknown)\n");
  }

  if (ntohs (p->ethhdr.h_proto) != ETH_P_PPPOE_DISC) {
    print_hex ((unsigned char *) (p + 1), ntohs (p->length));
    return;
  }


  while (t < (struct pppoe_tag *) ((char *) (p + 1) + ntohs (p->length))) {
    printf ("PPPoE tag:\ntype: %04x length: %04x ",
	    ntohs (t->type), ntohs (t->length));
    switch (ntohs (t->type)) {
    case TAG_END_OF_LIST:
      printf ("(End of list)\n");
      break;
    case TAG_SERVICE_NAME:
      printf ("(Service name)\n");
      break;
    case TAG_AC_NAME:
      printf ("(AC Name)\n");
      break;
    case TAG_HOST_UNIQ:
      printf ("(Host Uniq)\n");
      break;
    case TAG_AC_COOKIE:
      printf ("(AC Cookie)\n");
      break;
    case TAG_VENDOR_SPECIFIC:
      printf ("(Vendor Specific)\n");
      break;
    case TAG_RELAY_SESSION_ID:
      printf ("(Relay Session ID)\n");
      break;
    case TAG_SERVICE_NAME_ERROR:
      printf ("(Service Name Error)\n");
      break;
    case TAG_AC_SYSTEM_ERROR:
      printf ("(AC System Error)\n");
      break;
    case TAG_GENERIC_ERROR:
      printf ("(Generic Error)\n");
      break;
    default:
      printf ("(Unknown)\n");
    }
    if (ntohs (t->length) > 0)
      switch (ntohs (t->type)) {
      case TAG_SERVICE_NAME:
      case TAG_AC_NAME:
      case TAG_SERVICE_NAME_ERROR:
      case TAG_AC_SYSTEM_ERROR:
      case TAG_GENERIC_ERROR:	/* ascii data */
	buf = malloc (ntohs (t->length) + 1);
	memset (buf, 0, ntohs (t->length) + 1);
	strncpy (buf, (char *) (t + 1), ntohs (t->length));
	buf[ntohs (t->length)] = '\0';
	printf ("data (UTF-8): %s\n", buf);
	free (buf);
	break;

      case TAG_HOST_UNIQ:
      case TAG_AC_COOKIE:
      case TAG_RELAY_SESSION_ID:
	printf ("data (bin): ");
	for (i = 0; i < ntohs (t->length); i++)
	  printf ("%02x", (unsigned) *((char *) (t + 1) + i));
	printf ("\n");
	break;

      default:
	printf ("unrecognized data\n");
      }
    if (ntohs (t->type) == TAG_END_OF_LIST)
      break;
    t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->length));
  }
}

/* just for testing purposes ignore otherwise */
void
read_setup ()
{
/* should really populate the table with the params
   read from some config file; lets stick with one for now
 */
/* debugging for the 10.0.0.1 machine */
  unsigned char dstmac[6] = { 0x0, 0x0, 0xe8, 0x1c, 0x6, 0x9b };

  ses_table[0].pid = -1;
  ses_table[0].state = CONNECTED;
  ses_table[0].line = 0;
  ses_table[0].sid = 12;	/* session ID */
  strcpy (ses_table[0].name, "eth0");	/*name */
  memcpy (&ses_table[0].dmac, dstmac, 6);
}


int
create_raw_socket (unsigned short type)
{
  int optval = 1, rv;

  if ((rv = socket (PF_INET, SOCK_PACKET, htons (type))) < 0) {
    perror ("pppoe: socket");
    return -1;
  }

  if (setsockopt (rv, SOL_SOCKET, SO_BROADCAST, &optval, sizeof (optval)) < 0) {
    perror ("pppoe: setsockopt");
    return -1;
  }

  return rv;
}

int
get_hw_addr (int s, char *if_name, char *hw_addr)
{
  struct ifreq ifr;

  strncpy (ifr.ifr_name, if_name, sizeof (ifr.ifr_name));

  if (ioctl (s, SIOCGIFHWADDR, &ifr) < 0) {
    perror ("pppoe: ioctl(SIOCGIFHWADDR)");
    return -1;
  }

  if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
    fprintf (stderr, "pppoe: interface %s is not Ethernet!\n", if_name);
    return -1;
  }

  memcpy (hw_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

/*
   printf("<%s> we received hware address %x:%x:%x:%x:%x:%x\n",if_name,hw_addr[0],hw_addr[1], hw_addr[2],hw_addr[3],hw_addr[4],hw_addr[5]);

 */
  return 0;
}

int
create_padi (struct pppoe_packet **packet, const char *src, const char *name)
{
  int size;

  if (packet == NULL)
    return 0;

  if (*packet != NULL)
    free (*packet);

  size = sizeof (struct pppoe_packet) + sizeof (struct pppoe_tag);
  if (name != NULL)
    size += strlen (name);

  if ((*packet = malloc (size)) == NULL) {
    fprintf (stderr, "pppoe: malloc (create_padi)");
    return 0;
  }

  memcpy ((*packet)->ethhdr.h_dest, MAC_BCAST_ADDR, ETH_ALEN);
  memcpy ((*packet)->ethhdr.h_source, src, ETH_ALEN);
  (*packet)->ethhdr.h_proto = htons (ETH_P_PPPOE_DISC);
  (*packet)->ver = 1;
  (*packet)->type = 1;
  (*packet)->code = CODE_PADI;
  (*packet)->session = 0;
  (*packet)->length = htons (size - sizeof (struct pppoe_packet));

  /* fill out a blank service-name tag */
  (*(struct pppoe_tag *) (*packet + 1)).type = htons (TAG_SERVICE_NAME);

  (*(struct pppoe_tag *) (*packet + 1)).length =
    name ? htons (strlen (name)) : 0;
  if (name != NULL)
    memcpy ((char *) (*packet + 1) + sizeof (struct pppoe_tag), name,
	    strlen (name));

  return size;
}


int
create_padr (struct pppoe_packet **packet, const char *src, const char *dst,
	     char *name, struct pppoe_tag *extra_tag)
{
  int size;
  int size1 = 0, size2 = 0;
  char *p;
  struct pppoe_tag *t;

  if (packet == NULL)
    return 0;

  if (*packet != NULL)
    free (*packet);

  if (extra_tag)
    size1 = sizeof (struct pppoe_tag) + ntohs (extra_tag->length);

  size2 = sizeof (struct pppoe_tag);

  size = size1 + size2;

  if (name != NULL)
    size += strlen (name);

  if ((*packet = malloc (size + sizeof (struct pppoe_packet))) == NULL) {
    fprintf (stderr, "pppoe: malloc (create_padr)");
    return 0;
  }

  memcpy ((*packet)->ethhdr.h_dest, dst, ETH_ALEN);
  memcpy ((*packet)->ethhdr.h_source, src, ETH_ALEN);
  (*packet)->ethhdr.h_proto = htons (ETH_P_PPPOE_DISC);
  (*packet)->ver = 1;
  (*packet)->type = 1;
  (*packet)->code = CODE_PADR;
  (*packet)->session = 0;
  (*packet)->length = htons (size);

  if (extra_tag) {
    memcpy ((char *) (*packet + 1), extra_tag, size1);
    p = (char *) (*packet + 1);
    p += size1;
    t = (struct pppoe_tag *) p;
    /* fill out a blank service-name tag */
    t->type = htons (TAG_SERVICE_NAME);
    t->length = name ? htons (strlen (name)) : 0;
    if (name != NULL)
      memcpy ((char *) (p + sizeof (struct pppoe_tag)), name, strlen (name));

  }
  else {
    (*(struct pppoe_tag *) (*packet + 1)).type = htons (TAG_SERVICE_NAME);

    (*(struct pppoe_tag *) (*packet + 1)).length =
      name ? htons (strlen (name)) : 0;
    if (name != NULL)
      memcpy ((char *) (*packet + 1) + sizeof (struct pppoe_tag), name,
	      strlen (name));

  }

  return size + sizeof (struct pppoe_packet);
}


int
send_packet (int sock, struct pppoe_packet *packet, int len, const char *ifn)
{
  struct sockaddr addr;
  int c;

  memset (&addr, 0, sizeof (addr));
#ifdef DISC_DEB2
  printf ("\nsend_packet: ifn %s\n", ifn);
#endif
  strcpy (addr.sa_data, ifn);

#ifdef DISC_DEB1
  print_packet (packet);
#endif

  if ((c = sendto (sock, packet, len, 0, &addr, sizeof (addr))) < 0)
    perror ("pppoe: sendto (send_packet)");

  return c;
}

int
wait_for_packet (int *sock, int ns, struct pppoe_packet **packet, int *len,
		 int num_ret)
{
  fd_set fdset;
  int i, maxs = -1;
  struct sockaddr_in from;
  socklen_t fromlen;
  struct timeval timeout;
  int retransmits = 0;
  int reason = -1;

/* 
   a num_ret == 0 implies block and wait for the packet
 */

  FD_ZERO (&fdset);

  while (1) {
    retransmits++;
    if (num_ret && retransmits > num_ret)
      return -1;
    if (retransmits <= num_ret) {
      timeout.tv_sec = 1 << retransmits;
      timeout.tv_usec = 0;
    }

    for (i = 0; i < ns; i++) {
      FD_SET (sock[i], &fdset);
      if (sock[i] > maxs)
	maxs = sock[i];
    }

    if (num_ret == 0)
      reason = select (maxs + 1, &fdset, NULL, NULL, NULL);
    else
      reason = select (maxs + 1, &fdset, NULL, NULL, &timeout);

    if (reason < 0) {
      perror ("pppoe: select (wait_for_packet)");
      return -1;
    }
    if (reason == 0) {		/* timeout */
      if (send_packet (*sock, ret_pkt, ret_pkt_size, if_name) < 0) {
	fprintf (stderr, "pppoe: unable to send PADI packet\n");
	return -1;
      }
      continue;			/* go back to the top */
    }

    /* reason has the number of ready descriptors */


    for (i = 0; i < ns; i++) {
      if (FD_ISSET (sock[i], &fdset)) {
	if (sock[i] < 3)	/* ie, it's an fd */
	  return sock[i];

	if (*packet != NULL)
	  free (*packet);

	if ((*packet = malloc (MAX_PACKET)) == NULL) {
	  fprintf (stderr, "pppoe: malloc (wait_for_packet");
	  return -1;
	}

	fromlen = sizeof (from);
	if (recvfrom (sock[i], *packet, MAX_PACKET, 0,
		      (struct sockaddr *) &from, &fromlen) < 0) {
	  perror ("pppoe: recv (wait_for_packet)");
	  return -1;
	}


	return sock[i];
      }
    }
  }
}


struct pppoe_tag *
scan_tag (int length, struct pppoe_tag *start_tag, unsigned short type)
{
  struct pppoe_tag *tag, *ret;


  tag = start_tag;
  while ((tag < (start_tag + length)) && (ntohs (tag->type) != 0)) {
#ifdef DISC_DEB2
    printf ("scan_tag: tag %p length %d tag->type %x\n", tag,
	    ntohs (tag->length), ntohs (tag->type));
    printf ("scan_tag data (bin): ");
    for (i = 0; i < ntohs (ret->length); i++)
      printf ("%02x", (unsigned) *((char *) (ret + 1) + i));
    printf ("\n");
    printf ("scan_tag data (bin): DONE");
#endif
    if (ntohs (tag->type) == type) {	/* got it */

#ifdef DISC_DEB2
      printf ("Found it!");
#endif
      if ((ret = malloc (sizeof (struct pppoe_tag) + tag->length)) == NULL) {
	printf ("Error Mallocing for AC tag \n");
	exit (-1);
      }

      memcpy (ret, tag, sizeof (struct pppoe_tag) + ntohs (tag->length));

      return ret;
    }
#ifdef DISC_DEB2
    printf ("scan_tag: not matched! \n");
#endif

    tag = (char *) (tag) + sizeof (struct pppoe_tag) + ntohs (tag->length);

  }

  return NULL;
}

int
server_discover (int lineid)
{

  char src_addr[ETH_ALEN];	/* source hardware address */
  char dst_addr[ETH_ALEN];	/* destination hardware address */

  int pkt_size;

  struct pppoe_packet *packet = NULL;
  struct pppoe_tag *tag = NULL;

  int state = CODE_PADI;



  /* main discovery loop */

  if (get_hw_addr (disc_sock, if_name, src_addr) != 0) {
    fprintf (stderr, "pppoe: unable to get hardware address\n");
    return -1;
  }


#if 0
  printf ("state is: %x if_name %s PADR_ret %d PADI_ret %d\n", state, if_name,
	  PADR_ret, PADI_ret);
  printf ("<%s> src hware address %x:%x:%x:%x:%x:%x\n", if_name, src_addr[0],
	  src_addr[1], src_addr[2], src_addr[3], src_addr[4], src_addr[5]);

  printf ("0name is: %s\n", ses_table[0].name);
  printf ("0MAC: %x:%x:%x:%x:%x:%x\n", ses_table[lineid].dmac[0] & 0xff,
	  ses_table[lineid].dmac[1] & 0xff, ses_table[lineid].dmac[2] & 0xff,
	  ses_table[lineid].dmac[3] & 0xff, ses_table[lineid].dmac[4] & 0xff,
	  ses_table[lineid].dmac[5] & 0xff);
#endif

  while (1) {
/*
   printf("\n State %x\n",state);
   read_setup();
   state=STATE_RUN; 
 */
    if (state == STATE_RUN)
      break;

    switch (state) {
    case CODE_PADI:		/* initiate ppp_connection */
#ifdef DISC_DEB
      printf ("Sending PADI\n");
#endif
      /* start the PPPoE session */
      if ((pkt_size = create_padi (&packet, src_addr, NULL)) == 0) {
	fprintf (stderr, "pppoe: unable to create PADI packet\n");
	exit (1);
      }
      /* send the PADI packet */
      if (send_packet (disc_sock, packet, pkt_size, if_name) < 0) {
	fprintf (stderr, "pppoe: unable to send PADI packet\n");
	exit (1);
      }

      ret_pkt = packet;
      ret_pkt_size = pkt_size;

      state = CODE_PADO;
      break;

    case CODE_PADO:		/* wait for PADO */
#ifdef DISC_DEB
      printf ("waiting for PADO\n");
#endif
      if (wait_for_packet (&disc_sock, 1, &packet, &pkt_size, PADI_ret)
	  != disc_sock || (packet->code != CODE_PADO &&
			   packet->code != CODE_PADT)) {
	if (packet->code == CODE_PADI)	/* just an echo */
	  return -1;
	fprintf (stderr, "pppoe: unexpected packet %x\n", packet->code);
	continue;
      }
      if (packet->code == CODE_PADT) {	/* early termination */
	state = CODE_PADT;
	continue;
      }
#ifdef DISC_DEB2
      printf ("PADO received!\n");
      print_packet (packet);
      printf ("PADO printed!\n");
#endif
      memcpy (dst_addr, packet->ethhdr.h_source, ETH_ALEN);
/* some really nasty coding here; try to re-use M Ostrowskis code
instead at some point
*/
      if (ntohs (packet->length) > 0)
	tag =
	  scan_tag (ntohs (packet->length),
		    (struct pppoe_tag *) (packet + 1), TAG_AC_COOKIE);

      state = CODE_PADR;
      break;

    case CODE_PADR:		/* send PADR */
#ifdef DISC_DEB
      printf ("Sending PADR\n");
#endif
      if ((pkt_size = create_padr (&packet, src_addr, dst_addr, NULL, tag))
	  == 0) {
	fprintf (stderr, "pppoe: unable to create PADR packet\n");
	exit (1);
      }
      if (tag) {
	free (tag);
	tag = NULL;
      }
      if (send_packet (disc_sock, packet, pkt_size, if_name) < 0) {
	fprintf (stderr, "pppoe: unable to send PADR packet\n");
	exit (1);
      }
      state = CODE_PADS;
      ret_pkt = packet;
      ret_pkt_size = pkt_size;
      break;

    case CODE_PADS:		/* wait for PADS */
#ifdef DISC_DEB
      printf ("Waiting for PADS\n");
#endif
      if (wait_for_packet (&disc_sock, 1, &packet, &pkt_size, PADR_ret)
	  != disc_sock || (packet->code != CODE_PADS &&
			   packet->code != CODE_PADT)) {
	if (packet->code == CODE_PADR)	/* just an echo */
	  return -1;
	fprintf (stderr, "pppoe: unexpected packet %x\n", packet->code);
	continue;
      }
      if (memcmp (packet->ethhdr.h_source, dst_addr, sizeof (dst_addr))
	  != 0)
	continue;		/* discard packets not from AC */
      if (packet->code == CODE_PADT) {	/* early termination */
	state = CODE_PADT;
	continue;
      }

      socks[0] = disc_sock;

      printf ("OK! Discovery is completed successfully\n");
      ses_table[lineid].line = lineid;
      ses_table[lineid].sid = packet->session;
      printf ("We got session ID %d %x\n", ntohs (packet->session),
	      ntohs (packet->session));
      memcpy (&ses_table[lineid].dmac, dst_addr, ETH_ALEN);
      printf ("AC MAC: %x:%x:%x:%x:%x:%x\n", ses_table[lineid].dmac[0] & 0xff,
	      ses_table[lineid].dmac[1] & 0xff,
	      ses_table[lineid].dmac[2] & 0xff,
	      ses_table[lineid].dmac[3] & 0xff,
	      ses_table[lineid].dmac[4] & 0xff,
	      ses_table[lineid].dmac[5] & 0xff);


      state = STATE_RUN;

/* should start a thread here which just waits for a PADT
   packet 
 */
      break;

    case STATE_RUN:		/* running */

      break;

    case CODE_PADT:		/* received a PADT */
      return -1;
      break;

    default:
      fprintf (stderr, "pppoe: invalid state %x", state);
      exit (1);
    }
  }
  return (0);
}

void
sigproc (int src)
{
  printf ("\n *** Receieved signal %d ***** \n", src);
  exit (1);
}



void
sigchild (int src)
{
  pid_t pid;
  int status;
  int i;
  pid = waitpid (-1, &status, WNOHANG);
  printf ("\n *** Child Received signal %d PID %d ***** \n", src, pid);
  printf ("status is: %d\n", status);
  if (pid < 1) {
    return;
  }
/* scan */
  for (i = 0; i < 16; i++) {
    if (ses_table[i].pid == pid) {
      printf ("Line id which died is: %d\n", i);
      ses_table[i].state = DISCONNECTED;
      break;
    }

  }

}


int
open_socket ()
{
  int sockid;
  sockid = socket (AF_INET, SOCK_DGRAM, 0);
  if (sockid < 0)
    exit (-1);			/* could do better than this */

  return sockid;

}

int
cntrl (lineid)
{
  struct ifreq ifr;

  ifr.ifr_addr.sa_family = AF_INET;
  ifr.ifr_hwaddr.sa_family = lineid;

/* double check the endianness here */
  ifr.ifr_hwaddr.sa_data[1] = (ses_table[lineid].sid >> 8) & 0xff;
  ifr.ifr_hwaddr.sa_data[0] = ses_table[lineid].sid & 0xff;
  printf ("\n cntrl: SessionID (%x%x) \n",
	  (unsigned short) ifr.ifr_hwaddr.sa_data[1],
	  (unsigned short) ifr.ifr_hwaddr.sa_data[0]);
  if (ioctl (ctrl_sock, PPPOEIOCSID, &ifr) < 0) {
    perror ("PPPOEIOCSID");
    return (-1);
  }

  printf ("Setting AC MAC to: ");
  printf (" %x:%x:%x:%x:%x:%x\n", ses_table[lineid].dmac[0] & 0xff,
	  ses_table[lineid].dmac[1] & 0xff, ses_table[lineid].dmac[2] & 0xff,
	  ses_table[lineid].dmac[3] & 0xff, ses_table[lineid].dmac[4] & 0xff,
	  ses_table[lineid].dmac[5] & 0xff);

  memcpy (&ifr.ifr_hwaddr.sa_data, ses_table[lineid].dmac, 6);
  if (ioctl (ctrl_sock, PPPOEIOCDMAC, &ifr) < 0) {
    perror ("PPPOEIOCDMAC");
    return (-1);
  }

  strcpy (ifr.ifr_name, ses_table[lineid].name);
  printf ("Setting device name to %s \n", ifr.ifr_name);

  if (ioctl (ctrl_sock, PPPOEIODID, &ifr) < 0) {
    perror ("PPPOEIODID");
    return (-1);
  }
  return (0);
}


/* **********************************************88
*/
int lineid = 0;
char *fname;
int num_restart = 1;		/* 1 restart means no restart */
char pppox[6];

int
ppp_connect (void)
{

  int pid;

restart:
  num_restart--;

  printf ("\nTotal retries to go %d\n", num_restart);

  if ((disc_sock = create_raw_socket (ETH_P_PPPOE_DISC)) < 0) {
    fprintf (stderr, "pppoe: unable to create raw socket\n");
    return 1;
  }


/* we block until we extablish a ppp_connection
   in this version
 */

  if (server_discover (lineid) < 0) {
//    printf (" sorry we died \n");
    printf ("PPPoE connection timeout - aborting...\n");
    exit (-1);
  }

  close (disc_sock);		/* dont need it anymore */
/* at this point we know the destination MAC address,
   the session ID,
   the device,
 */

  printf ("MAC: %x:%x:%x:%x:%x:%x\n", ses_table[lineid].dmac[0] & 0xff,
	  ses_table[lineid].dmac[1] & 0xff, ses_table[lineid].dmac[2] & 0xff,
	  ses_table[lineid].dmac[3] & 0xff, ses_table[lineid].dmac[4] & 0xff,
	  ses_table[lineid].dmac[5] & 0xff);


#if 0
/* for debugging only, turn this off in the real thing */
/* populate table */

  read_setup ();
#endif
  ctrl_sock = open_socket ();

/* download the cntrl info for this lineid */
  cntrl (lineid);

  close (ctrl_sock);

  signal (SIGINT, &sigproc);
  signal (SIGTERM, &sigproc);
  signal (SIGCHLD, &sigchild);

/* 
   now lets fire the PPP session
   TODO: use pthreads 
 */


  pid = fork ();


  if (ses_table[lineid].pid < 0) {
    printf ("pppd: unable to fork(), abandoning!\n");
    /* call some cleanup routine */
    exit (-1);
  }

  ses_table[lineid].pid = pid;
  if (!pid) {
    /* the speed is meaningless really, but pppd insists */
    printf ("\nstarting pppd using options file: %s to pppox interface %s\n",
	    fname, pppox);

    if (execl ("/usr/sbin/pppd", "-d", pppox, "38400", "file", fname, NULL) <
	0) {
      perror ("PPP");
      exit (-1);
    }
  }

  printf ("\nPPP started pid is: %d\n", ses_table[lineid].pid + 1);

/* 
   sit here and asynchronously wait for a PADT
   or issue a PADT should the user decide to terminate the ppp_connection
   Hmmmm ... maybe we should have an option to restart the ppp_connection
   if it dies
 */

/* move to ppp_connected state only after pppd has been started */
  ses_table[lineid].state = CONNECTED;

  pause ();

  ses_table[lineid].state = DISCONNECTED;
  printf ("\n OK we got killed\n");

  if (!num_restart)
    exit (0);

  goto restart;
}

int
print_help ()
{
  printf ("\npppoe version %d.%d build %d\n", VERSION_MAJOR, VERSION_MINOR,
	  VERSION_DATE);
  printf ("recognized options are: \n");
  printf (" -I <interface> : overides the default interface of eth0\n");
  printf
    (" -P <pppox_num> : overides the default pppox of pppox0. Used to have multiple sessions\n");
  printf (" -S : starts pppoed in server mode\n");
  printf
    (" -R <num_retries>: forces pppoed to be restarted num_retries should the other end be detected to be dead. Needs lcp_echo. Read the INSTALL file instructions\n");
  printf
    (" -F <filneame> : overides the default ppp options file of /etc/ppp/options \n");
  printf (" -L <filneame> : sets a log file where verbose output is sent \n");
  printf ("\n");
  exit (0);
}


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

  int opt;
/* only worry about a single ppp_connection (ppp0) in this version
 */
/* the number of PADI and PADR retransmits.
   We should be able to use the command line options to pass these instead
 */
  PADR_ret = 5;
  PADI_ret = 5;

  if ((if_name = malloc (strlen (("eth0") + 1))) == NULL) {
    printf ("Error Mallocing for default ifname\n");
    exit (-1);
  }
  if ((fname = malloc (256)) == NULL) {
    printf ("Error Mallocing for default ifname\n");
    exit (-1);
  }
/* defaults to eth0 */
  strcpy (if_name, "eth0");
  strcpy (fname, "/etc/ppp/options");

  /* parse options */

  while ((opt = getopt (argc, argv, "R:I:F:L:V:P:S")) != -1)
    switch (opt) {
    case 'R':			/* sets number of retries */
      num_restart = strtol (optarg, (char **) NULL, 10);
      num_restart += 1;
      break;
    case 'I':			/* sets interface */
      strcpy (if_name, optarg);
      opt_verbose = 1;
      break;
    case 'F':			/* sets the options file */
      if (strlen (optarg) > 255) {
	printf ("filenname cannot exceed 256 characters\n");
	exit (-1);
      }
      strcpy (fname, optarg);
      break;
    case 'L':			/* log file */
      opt_verbose = 1;
      if (log_file != NULL)
	fclose (log_file);
      if ((log_file = fopen (optarg, "w")) == NULL) {
	fprintf (stderr, "fopen\n");
	exit (1);
      }
      break;
    case 'P':			/* lineid */
      lineid = strtol (optarg, (char **) NULL, 10);
      printf ("lineid set to %s %d\n", optarg, lineid);
      break;
    case '?':			/* version */
    case 'V':			/* version */
      printf ("\npppoe version %d.%d build %d\n", VERSION_MAJOR,
	      VERSION_MINOR, VERSION_DATE);
      exit (0);
      break;
    case 'S':			/* server mode */
      printf ("pppoe server mode not supported in this version\n");
      exit (0);
      break;
    default:
      fprintf (stderr, "Unknown option %c\n", optopt);
      print_help ();
      exit (1);
    }

  strcpy (ses_table[lineid].name, if_name);
/*
 strcpy (pppox, "pppox");
*/
/* careful with overflows here; this runs as root at 
the moment, so OK */
  sprintf (pppox, "pppox%d", lineid);

  ppp_connect ();

  exit (0);
}
