/*
 * 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: duplicate_set.c,v 1.16 2007/09/17 22:24:22 bernd67 Exp $
 */



#include "defs.h"
#include "duplicate_set.h"
#include "scheduler.h"
#include "olsr.h"

/* The duplicate table */
static struct dup_entry dup_set[HASHSIZE];

static float dup_hold_time = DUP_HOLD_TIME;

/**
 *Initialize the duplicate table entrys
 *
 *@return nada
 */
void
olsr_init_duplicate_table(void)
{
  int i;

  OLSR_PRINTF(3, "Initializing duplicatetable - hashsize %d\n", HASHSIZE);

  /* Since the holdingtime is rather large for duplicate
   * entries the timeoutfunction is only ran every 2 seconds
   */
  olsr_register_scheduler_event_dijkstra(&olsr_time_out_duplicate_table, NULL, 2, 0, NULL);
  
  for(i = 0; i < HASHSIZE; i++)
    {
      dup_set[i].next = &dup_set[i];
      dup_set[i].prev = &dup_set[i];
    }
}


/**
 *Add an entry to the duplicate set. The set is not checked
 *for duplicate entries.
 *
 *@param originator IP address of the sender of the message
 *@param seqno seqno of the message
 *
 *@return positive on success
 */
struct dup_entry *
olsr_add_dup_entry(union olsr_ip_addr *originator, olsr_u16_t seqno)
{
  olsr_u32_t hash;
  struct dup_entry *new_dup_entry;


  /* Hash the senders address */
  hash = HASHMASK & seqno;

  new_dup_entry = olsr_malloc(sizeof(struct dup_entry), "New dup entry");

  /* Address */
  COPY_IP(&new_dup_entry->addr, originator);
  /* Seqno */
  new_dup_entry->seqno = seqno;
  /* Set timer */
  new_dup_entry->timer = GET_TIMESTAMP(dup_hold_time*1000);
  /* Interfaces */
  new_dup_entry->ifaces = NULL;
  /* Forwarded */
  new_dup_entry->forwarded = 0;

  /* Insert into set */
  QUEUE_ELEM(dup_set[hash], new_dup_entry);
  /*
  dup_set[hash].next->prev = new_dup_entry;
  new_dup_entry->next = dup_set[hash].next;
  dup_set[hash].next = new_dup_entry;
  new_dup_entry->prev = &dup_set[hash];
  */
  return new_dup_entry;
}


/**
 * Check wether or not a message should be processed
 *
 */
int
olsr_check_dup_table_proc(union olsr_ip_addr *originator, olsr_u16_t seqno)
{
  olsr_u32_t hash;
  struct dup_entry *tmp_dup_table;

  /* Hash the senders address */
  hash = HASHMASK & seqno;

  /* Check for entry */
  for(tmp_dup_table = dup_set[hash].next;
      tmp_dup_table != &dup_set[hash];
      tmp_dup_table = tmp_dup_table->next)
    {
      if(COMP_IP(&tmp_dup_table->addr, originator) &&
	 (tmp_dup_table->seqno == seqno))
	{
	  return 0;
	}
    }

  return 1;
}



/**
 * Check wether or not a message should be forwarded
 *
 */
int
olsr_check_dup_table_fwd(union olsr_ip_addr *originator, 
		     olsr_u16_t seqno,
		     union olsr_ip_addr *int_addr)
{
  olsr_u32_t hash;
  struct dup_entry *tmp_dup_table;

  /* Hash the senders address */
  hash = HASHMASK & seqno;

  /* Check for entry */
  for(tmp_dup_table = dup_set[hash].next;
      tmp_dup_table != &dup_set[hash];
      tmp_dup_table = tmp_dup_table->next)
    {
      if(COMP_IP(&tmp_dup_table->addr, originator) &&
	 (tmp_dup_table->seqno == seqno))
	{
	  struct dup_iface *tmp_dup_iface;
	  /* Check retransmitted */
	  if(tmp_dup_table->forwarded)
	    return 0;
	  /* Check for interface */
	  tmp_dup_iface = tmp_dup_table->ifaces;
	  while(tmp_dup_iface)
	    {
	      if(COMP_IP(&tmp_dup_iface->addr, int_addr))
		return 0;
	      
	      tmp_dup_iface = tmp_dup_iface->next;
	    }
	}
    }
  

  return 1;
}




/**
 *Delete and dequeue a duplicate entry
 *
 *@param entry the entry to delete
 *
 */
void
olsr_del_dup_entry(struct dup_entry *entry)
{
  struct dup_iface *tmp_iface, *del_iface;

  tmp_iface = entry->ifaces;

  /* Free interfaces */
  while(tmp_iface)
    {
      del_iface = tmp_iface;
      tmp_iface = tmp_iface->next;
      free(del_iface);
    }

  /* Dequeue */
  DEQUEUE_ELEM(entry);
  free(entry);
}



void
olsr_time_out_duplicate_table(void *foo __attribute__((unused)))
{
  int i;

  for(i = 0; i < HASHSIZE; i++)
    {      
      struct dup_entry *tmp_dup_table;
      tmp_dup_table = dup_set[i].next;

      while(tmp_dup_table != &dup_set[i])
	{
	  if(TIMED_OUT(tmp_dup_table->timer))
	    {
	      struct dup_entry *entry_to_delete = tmp_dup_table;
#ifdef DEBUG
	      OLSR_PRINTF(5, "DUP TIMEOUT[%s] s: %d\n", 
		          olsr_ip_to_string(&tmp_dup_table->addr),
		          tmp_dup_table->seqno);
#endif
	      tmp_dup_table = tmp_dup_table->next;
	      olsr_del_dup_entry(entry_to_delete);
	    }
	  else
	    {
	      tmp_dup_table = tmp_dup_table->next;
	    }
	}
    }
}




int
olsr_update_dup_entry(union olsr_ip_addr *originator, 
		      olsr_u16_t seqno, 
		      union olsr_ip_addr *iface)
{
  olsr_u32_t hash;
  struct dup_entry *tmp_dup_table;
  struct dup_iface *new_iface;

  /* Hash the senders address */
  hash = HASHMASK & seqno;


  /* Check for entry */
  for(tmp_dup_table = dup_set[hash].next;
      tmp_dup_table != &dup_set[hash];
      tmp_dup_table = tmp_dup_table->next)
    {
      if(COMP_IP(&tmp_dup_table->addr, originator) &&
	 (tmp_dup_table->seqno == seqno))
	{
	  break;
	}
    }

  if(tmp_dup_table == &dup_set[hash])
    /* Did not find entry - create it */
    tmp_dup_table = olsr_add_dup_entry(originator, seqno);
  
  /* 0 for now */
  tmp_dup_table->forwarded = 0;
  
  new_iface = olsr_malloc(sizeof(struct dup_iface), "New dup iface");

  COPY_IP(&new_iface->addr, iface);
  new_iface->next = tmp_dup_table->ifaces;
  tmp_dup_table->ifaces = new_iface;
  
  /* Set timer */
  tmp_dup_table->timer = GET_TIMESTAMP(dup_hold_time*1000);
  
  return 1;
}




int
olsr_set_dup_forward(union olsr_ip_addr *originator, 
		     olsr_u16_t seqno)
{
  olsr_u32_t hash;
  struct dup_entry *tmp_dup_table;

  /* Hash the senders address */
  hash = HASHMASK & seqno;

  /* Check for entry */
  for(tmp_dup_table = dup_set[hash].next;
      tmp_dup_table != &dup_set[hash];
      tmp_dup_table = tmp_dup_table->next)
    {
      if(COMP_IP(&tmp_dup_table->addr, originator) &&
	 (tmp_dup_table->seqno == seqno))
	{
	  break;
	}
    }

  if(tmp_dup_table == &dup_set[hash])
    /* Did not find entry !! */
    return 0;
  
#ifdef DEBUG
  OLSR_PRINTF(3, "Setting DUP %s/%d forwarded\n", olsr_ip_to_string(&tmp_dup_table->addr), seqno);
#endif

  /* Set forwarded */
  tmp_dup_table->forwarded = 1;
  
  /* Set timer */
  tmp_dup_table->timer = GET_TIMESTAMP(dup_hold_time*1000);
  
  return 1;
}

void
olsr_print_duplicate_table(void)
{
  int i;

  printf("\nDUP TABLE:\n");

  for(i = 0; i < HASHSIZE; i++)
    {      
      struct dup_entry *tmp_dup_table = dup_set[i].next;
      
      //printf("Timeout %d %d\n", i, j);
      while(tmp_dup_table != &dup_set[i])
	{
	  printf("[%s] s: %d\n", 
		 olsr_ip_to_string(&tmp_dup_table->addr),
		 tmp_dup_table->seqno);
	  tmp_dup_table = tmp_dup_table->next;
	}
    }
  printf("\n");

}


syntax highlighted by Code2HTML, v. 0.9.1