/*
 * The olsr.org Optimized Link-State Routing daemon(olsrd)
 * Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright 
 *   notice, this list of conditions and the following disclaimer in 
 *   the documentation and/or other materials provided with the 
 *   distribution.
 * * Neither the name of olsr.org, olsrd nor the names of its 
 *   contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Visit http://www.olsr.org for more information.
 *
 * If you find this software useful feel free to make a donation
 * to the project. For more information see the website or contact
 * the copyright holders.
 *
 * $Id: link_layer.c,v 1.14 2007/08/19 23:00:15 bernd67 Exp $
 */


#if 0 /* DEPRECATED - KEPT FOR REFERENCE */

/* Ugly fix to make this compile on wireless extentions < 16 */
#define _LINUX_ETHTOOL_H

#include "../link_layer.h"
#include "../olsr_protocol.h"
#include "../scheduler.h"
#include "../interfaces.h"
#include <linux/wireless.h>
#include <linux/icmp.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include <unistd.h>
#include <stdlib.h>

#include "olsr_protocol.h"

void
init_link_layer_notification(void);

void
poll_link_layer(void *);

int
add_spy_node(union olsr_ip_addr *, char *);

#define	MAXIPLEN	60
#define	MAXICMPLEN	76

float poll_int = 0.2;

int
iw_get_range_info(char *, struct iw_range *);

int
clear_spy_list(char *);

int
convert_ip_to_mac(union olsr_ip_addr *, struct sockaddr *, char *);

void
send_ping(union olsr_ip_addr *);


void
init_link_layer_notification()
{
  struct interface *ifd;

  OLSR_PRINTF(1, "Initializing link-layer notification...\n");


  for (ifd = ifnet; ifd ; ifd = ifd->int_next) 
    {
      if(ifd->is_wireless)
	clear_spy_list(ifd->int_name);
    }

  olsr_register_scheduler_event(&poll_link_layer, NULL, poll_int, 0, NULL);

  return;
}

int
clear_spy_list(char *ifname)
{
  struct iwreq	wrq;

  /* Time to do send addresses to the driver */
  wrq.u.data.pointer = NULL;//(caddr_t) hw_address;
  wrq.u.data.length = 0;
  wrq.u.data.flags = 0;

  /* Set device name */
  strncpy(wrq.ifr_name, ifname, IFNAMSIZ);

  if(ioctl(olsr_cnf->ioctl_s, SIOCSIWSPY, &wrq) < 0)
    {
      OLSR_PRINTF(1, "Could not clear spylist %s\n", strerror(errno));
      return -1;
    }

  return 1;
}



int
add_spy_node(union olsr_ip_addr *addr, char *interface)
{
  struct sockaddr       new_node;
  struct iwreq		wrq;
  int			nbr;		/* Number of valid addresses */
  struct sockaddr	hw_address[IW_MAX_SPY];
  char	buffer[(sizeof(struct iw_quality) +
		sizeof(struct sockaddr)) * IW_MAX_SPY];
  
  OLSR_PRINTF(1, "Adding spynode!\n\n");
  
  /* get all addresses already in the driver */

  wrq.u.data.pointer = (caddr_t) buffer;
  wrq.u.data.length = IW_MAX_SPY;
  wrq.u.data.flags = 0;

  strncpy(wrq.ifr_name, interface, IFNAMSIZ);

  if(ioctl(olsr_cnf->ioctl_s, SIOCGIWSPY, &wrq) < 0)
    {
      OLSR_PRINTF(1, "Could not get old spylist %s\n", strerror(errno));
      return 0;
    }

  /* Copy old addresses */
  nbr = wrq.u.data.length;
  memcpy(hw_address, buffer, nbr * sizeof(struct sockaddr));

  OLSR_PRINTF(1, "Old addresses: %d\n\n", nbr);

  /* Check upper limit */
  if(nbr >= IW_MAX_SPY)
    return 0;

  /* Add new address if MAC exists in ARP cache */
  if(convert_ip_to_mac(addr, &new_node, interface) > 0)
    {
      memcpy(&hw_address[nbr], &new_node, sizeof(struct sockaddr));
      nbr++;
    }
  else
    return 0;
  
  /* Add all addresses */
  wrq.u.data.pointer = (caddr_t) hw_address;
  wrq.u.data.length = nbr;
  wrq.u.data.flags = 0;
  
  /* Set device name */
  strncpy(wrq.ifr_name, interface, IFNAMSIZ);
  
  if(ioctl(olsr_cnf->ioctl_s, SIOCSIWSPY, &wrq) < 0)
    {
      OLSR_PRINTF(1, "Could not clear spylist %s\n", strerror(errno));
      return 0;
    }


  return 1;
}


int
convert_ip_to_mac(union olsr_ip_addr *ip, struct sockaddr *mac, char *interface)
{
  struct arpreq	arp_query;
  struct sockaddr_in tmp_sockaddr;


  memset(&arp_query, 0, sizeof(struct arpreq));

  OLSR_PRINTF(1, "\nARP conversion for %s interface %s\n", 
	      olsr_ip_to_string(ip),
	      interface);

  tmp_sockaddr.sin_family = AF_INET;
  tmp_sockaddr.sin_port = 0;

  memcpy(&tmp_sockaddr.sin_addr, ip, olsr_cnf->ipsize);

  /* Translate IP addresses to MAC addresses */
  memcpy(&arp_query.arp_pa, &tmp_sockaddr, sizeof(struct sockaddr_in));
  arp_query.arp_ha.sa_family = 0;
  arp_query.arp_flags = 0;

  strncpy(arp_query.arp_dev, interface, IFNAMSIZ);
  
  if((ioctl(olsr_cnf->ioctl_s, SIOCGARP, &arp_query) < 0) ||
     !(arp_query.arp_flags & ATF_COM)) /* ATF_COM - hw addr valid */
    {
      OLSR_PRINTF(1, "Arp failed: (%s) - trying lookup\n", strerror(errno));

      /* No address - create a thread that sends a PING */
      send_ping(ip);
  
      return -1;
    }

  OLSR_PRINTF(1, "Arp success!\n");

  memcpy(mac, &arp_query.arp_ha, sizeof(struct sockaddr));

  return 1;
}



/**
 *A thread that sends a ICMP echo "ping" packet
 *to a given destination to force the ARP cache
 *to be updated... kind of a kludge....
 *
 *@param _ip the IP address to ping
 */
/* ONLY IPv4 FOR NOW!!! */

void
send_ping(union olsr_ip_addr *ip)
{
  int ping_s;
  struct sockaddr dst;
  struct sockaddr_in *dst_in;
  char *packet;
  struct icmphdr *icp;

  dst_in = (struct sockaddr_in *) &dst;

  dst_in->sin_family = AF_INET;
  memcpy(&dst_in->sin_addr, ip, olsr_cnf->ipsize);

  OLSR_PRINTF(1, "pinging %s\n\n", olsr_ip_to_string(ip));

  if ((ping_s = socket(AF_INET, SOCK_RAW, PF_INET)) < 0) 
    {
      OLSR_PRINTF(1, "Could not create RAW socket for ping!\n%s\n", strerror(errno));
      return;
    }

  /* Create packet */
  packet = malloc(MAXIPLEN + MAXICMPLEN);
  
  
  icp = (struct icmphdr *)packet;
  icp->type = ICMP_ECHO;
  icp->code = 0;
  icp->checksum = 0;
  icp->un.echo.sequence = 1;
  icp->un.echo.id = getpid() & 0xFFFF;

  if((sendto(ping_s, packet, MAXIPLEN + MAXICMPLEN + 8, 0, &dst, sizeof(struct sockaddr))) !=
     MAXIPLEN + MAXICMPLEN + 8)
    {
      OLSR_PRINTF(1, "Error PING: %s\n", strerror(errno));
    }

  /* Nevermind the pong ;-) */

  OLSR_PRINTF(1, "Ping complete...\n");
  close(ping_s);

  free(packet);

  return;
}

void
poll_link_layer(void *foo)
{
  struct iwreq		wrq;
  char		        buffer[(sizeof(struct iw_quality) +
			       sizeof(struct sockaddr)) * IW_MAX_SPY];
  struct sockaddr       *hwa;
  struct iw_quality     *qual;
  int		        n;
  struct iw_range	range;
  int		        i, j;
  int                   has_range = 0;
  struct interface      *iflist;

  //OLSR_PRINTF(1, "Polling link-layer notification...\n");

  for(iflist = ifnet; iflist != NULL; iflist = iflist->int_next)
    {
      if(!iflist->is_wireless)
	continue;

      /* Collect stats */
      wrq.u.data.pointer = (caddr_t) buffer;
      wrq.u.data.length = IW_MAX_SPY;
      wrq.u.data.flags = 0;
      
      /* Set device name */
      strncpy(wrq.ifr_name, iflist->int_name, IFNAMSIZ);
      
      /* Do the request */
      if(ioctl(olsr_cnf->ioctl_s, SIOCGIWSPY, &wrq) < 0)
	{
          OLSR_PRINTF(1, "%-8.16s  Interface doesn't support wireless statistic collection\n\n", iflist->int_name);
	  return;
	}
      
      /* Get range info if we can */
      if(iw_get_range_info(iflist->int_name, &(range)) >= 0)
	has_range = 1;
      
      /* Number of addresses */
      n = wrq.u.data.length;
      
      /* The two lists */
      hwa = (struct sockaddr *) buffer;
      qual = (struct iw_quality *) (buffer + (sizeof(struct sockaddr) * n));
      
      for(i = 0; i < n; i++)
	{
	  if(!(qual->updated & 0x7))
	    continue;
	  
	  /* Print stats for each address */
	  OLSR_PRINTF(1, "MAC");
	  for(j = 0; j < 6; j++)
	    {
                    OLSR_PRINTF(1, ":%02x", (hwa[i].sa_data[j] % 0xffffff00));
	    }
	  if(!has_range)
	    OLSR_PRINTF(1, " : Quality:%d  Signal level:%d dBm  Noise level:%d dBm",
			qual[i].qual,
			qual[i].level - 0x100, 
			qual[i].noise - 0x100);
	  else
	    OLSR_PRINTF(1, " : Quality:%d/%d  Signal level:%d dBm  Noise level:%d dBm",
			qual[i].qual,
			range.max_qual.qual,
			qual[i].level - 0x100, 
			qual[i].noise - 0x100);
	  
	  OLSR_PRINTF(1, "\n");
	  
	}
    }

  //OLSR_PRINTF(1, "\n");
  return;
}





/*
 * Get the range information out of the driver
 */
int
iw_get_range_info(char            *ifname,
		  struct iw_range *range)
{
  struct iwreq		wrq;
  char			buffer[sizeof(struct iw_range) * 2];	/* Large enough */
  union iw_range_raw    *range_raw;

  /* Cleanup */
  bzero(buffer, sizeof(buffer));

  wrq.u.data.pointer = (caddr_t) buffer;
  wrq.u.data.length = sizeof(buffer);
  wrq.u.data.flags = 0;

  /* Set device name */
  strncpy(wrq.ifr_name, ifname, IFNAMSIZ);

  if(ioctl(olsr_cnf->ioctl_s, SIOCGIWRANGE, &wrq) < 0)
    {
      OLSR_PRINTF(1, "NO RANGE\n");
      return -1;
    }

  /* Point to the buffer */
  range_raw = (union iw_range_raw *) buffer;

  memcpy((char *) range, buffer, sizeof(struct iw_range));

  return 1;
}


#endif


syntax highlighted by Code2HTML, v. 0.9.1