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

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


struct hna_entry hna_set[HASHSIZE];
size_t netmask_size;


/**
 *Initialize the HNA set
 */
int
olsr_init_hna_set(void)
{

  int idx;

  if(olsr_cnf->ip_version == AF_INET)
    {
      netmask_size = sizeof(olsr_u32_t);
    }
  else
    {
      netmask_size = sizeof(olsr_u16_t);
    }

  /* Since the holdingtime is assumed to be rather large for 
   * HNA entries, the timeoutfunction is only ran once every second
   */
  olsr_register_scheduler_event_dijkstra(&olsr_time_out_hna_set, NULL, 1, 0, NULL);

  for(idx=0;idx<HASHSIZE;idx++)
    {
      hna_set[idx].next = &hna_set[idx];
      hna_set[idx].prev = &hna_set[idx];
    }

  return 1;
}

int
olsr_get_hna_prefix_len(struct hna_net *hna)
{
  if (olsr_cnf->ip_version == AF_INET) {
    return olsr_netmask_to_prefix((union olsr_ip_addr *)&hna->A_netmask.v4);
  } else {
    return hna->A_netmask.v6;
  }
}


/**
 *Lookup a network entry in a networkentry list
 *
 *@param nets the network list to look in
 *@param net the network to look for
 *@param mask the netmask to look for
 *
 *@return the localted entry or NULL of not found
 */
struct hna_net *
olsr_lookup_hna_net(struct hna_net *nets, union olsr_ip_addr *net, union hna_netmask *mask)
{
  struct hna_net *tmp_net;


  /* Loop trough entrys */
  for(tmp_net = nets->next;
      tmp_net != nets;
      tmp_net = tmp_net->next)
    { 
      if(COMP_IP(&tmp_net->A_network_addr, net) &&
	 (memcmp(&tmp_net->A_netmask, mask, netmask_size) == 0))
	return tmp_net;
    }
  
  /* Not found */
  return NULL;
}




/**
 *Lookup a gateway entry
 *
 *@param gw the address of the gateway
 *
 *@return the located entry or NULL if not found
 */
struct hna_entry *
olsr_lookup_hna_gw(union olsr_ip_addr *gw)
{
  struct hna_entry *tmp_hna;
  olsr_u32_t hash;

  //OLSR_PRINTF(5, "TC: lookup entry\n");

  hash = olsr_hashing(gw);
  
  /* Check for registered entry */
  for(tmp_hna = hna_set[hash].next;
      tmp_hna != &hna_set[hash];
      tmp_hna = tmp_hna->next)
    {
      if(COMP_IP(&tmp_hna->A_gateway_addr, gw))
	return tmp_hna;
    }
  
  /* Not found */
  return NULL;
}



/**
 *Add a gatewayentry to the HNA set
 *
 *@param addr the address of the gateway
 *
 *@return the created entry
 */
struct hna_entry *
olsr_add_hna_entry(union olsr_ip_addr *addr)
{
  struct hna_entry *new_entry;
  olsr_u32_t hash;

  new_entry = olsr_malloc(sizeof(struct hna_entry), "New HNA entry");

  /* Fill struct */
  COPY_IP(&new_entry->A_gateway_addr, addr);

  /* Link nets */
  new_entry->networks.next = &new_entry->networks;
  new_entry->networks.prev = &new_entry->networks;

  /* queue */
  hash = olsr_hashing(addr);
  
  hna_set[hash].next->prev = new_entry;
  new_entry->next = hna_set[hash].next;
  hna_set[hash].next = new_entry;
  new_entry->prev = &hna_set[hash];

  return new_entry;

}



/**
 *Adds a ntework entry to a HNA gateway
 *
 *@param hna_gw the gateway entry to add the
 *network to
 *@param net the networkaddress to add
 *@param mask the netmask
 *
 *@return the newly created entry
 */
struct hna_net *
olsr_add_hna_net(struct hna_entry *hna_gw, union olsr_ip_addr *net, union hna_netmask *mask)
{
  struct hna_net *new_net;


  /* Add the net */
  new_net = olsr_malloc(sizeof(struct hna_net), "Add HNA net");
  
  /* Fill struct */
  COPY_IP(&new_net->A_network_addr, net);
  memcpy(&new_net->A_netmask, mask, netmask_size);

  /* Queue */
  hna_gw->networks.next->prev = new_net;
  new_net->next = hna_gw->networks.next;
  hna_gw->networks.next = new_net;
  new_net->prev = &hna_gw->networks;

  return new_net;
}




/**
 * Update a HNA entry. If it does not exist it
 * is created.
 * This is the only function that should be called 
 * from outside concerning creation of HNA entries.
 *
 *@param gw address of the gateway
 *@param net address of the network
 *@param mask the netmask
 *@param vtime the validitytime of the entry
 *
 *@return nada
 */
void
olsr_update_hna_entry(union olsr_ip_addr *gw, union olsr_ip_addr *net, union hna_netmask *mask, float vtime)
{
  struct hna_entry *gw_entry;
  struct hna_net *net_entry;

  if((gw_entry = olsr_lookup_hna_gw(gw)) == NULL)
    /* Need to add the entry */
    gw_entry = olsr_add_hna_entry(gw);
  
  if((net_entry = olsr_lookup_hna_net(&gw_entry->networks, net, mask)) == NULL)
    {
      /* Need to add the net */
      net_entry = olsr_add_hna_net(gw_entry, net, mask);
      changes_hna = OLSR_TRUE;
    }

  /* Update holdingtime */
  net_entry->A_time = GET_TIMESTAMP(vtime*1000);

}






/**
 *Function that times out all entrys in the hna set and
 *deletes the timed out ones.
 *
 *@return nada
 */
void
olsr_time_out_hna_set(void *foo __attribute__((unused)))
{
  int idx;

  for(idx=0;idx<HASHSIZE;idx++)
    {
      struct hna_entry *tmp_hna = hna_set[idx].next;
      /* Check all entrys */
      while(tmp_hna != &hna_set[idx])
	{
	  /* Check all networks */
	  struct hna_net *tmp_net = tmp_hna->networks.next;

	  while(tmp_net != &tmp_hna->networks)
	    {
	      if(TIMED_OUT(tmp_net->A_time))
		{
		  struct hna_net *net_to_delete = tmp_net;
		  tmp_net = tmp_net->next;
		  DEQUEUE_ELEM(net_to_delete);
		  free(net_to_delete);
		  changes_hna = OLSR_TRUE;
		}
	      else
		tmp_net = tmp_net->next;
	    }

	  /* Delete gw entry if empty */
	  if(tmp_hna->networks.next == &tmp_hna->networks)
	    {
	      struct hna_entry *hna_to_delete = tmp_hna;
	      tmp_hna = tmp_hna->next;

	      /* Dequeue */
	      DEQUEUE_ELEM(hna_to_delete);
	      /* Delete */
	      free(hna_to_delete);
	    }
	  else
	    tmp_hna = tmp_hna->next;
	}
    }

}



/**
 *Function that times out all entrys in the hna set and
 *deletes the timed out ones.
 *
 *@return nada
 */
void
olsr_print_hna_set(void)
{
  int idx;

  OLSR_PRINTF(1, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- HNA SET\n\n",
              nowtm->tm_hour,
              nowtm->tm_min,
              nowtm->tm_sec,
	      (int)now.tv_usec/10000);
  
  if(olsr_cnf->ip_version == AF_INET)
    OLSR_PRINTF(1, "IP net          netmask         GW IP\n");
  else
    OLSR_PRINTF(1, "IP net/prefixlen               GW IP\n");

  for(idx=0;idx<HASHSIZE;idx++)
    {
      struct hna_entry *tmp_hna = hna_set[idx].next;
      /* Check all entrys */
      while(tmp_hna != &hna_set[idx])
	{
	  /* Check all networks */
	  struct hna_net *tmp_net = tmp_hna->networks.next;

	  while(tmp_net != &tmp_hna->networks)
	    {
	      if(olsr_cnf->ip_version == AF_INET)
		{
		  OLSR_PRINTF(1, "%-15s ", olsr_ip_to_string(&tmp_net->A_network_addr));
		  OLSR_PRINTF(1, "%-15s ", olsr_ip_to_string((union olsr_ip_addr *)&tmp_net->A_netmask.v4));
		  OLSR_PRINTF(1, "%-15s\n", olsr_ip_to_string(&tmp_hna->A_gateway_addr));
		}
	      else
		{
		  OLSR_PRINTF(1, "%-27s/%d", olsr_ip_to_string(&tmp_net->A_network_addr), tmp_net->A_netmask.v6);
		  OLSR_PRINTF(1, "%s\n", olsr_ip_to_string(&tmp_hna->A_gateway_addr));
		}

	      tmp_net = tmp_net->next;
	    }
	  tmp_hna = tmp_hna->next;
	}
    }

}

/*
 * Local Variables:
 * c-basic-offset: 2
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1