/*
 * 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: net_olsr.c,v 1.30 2007/09/16 21:20:16 bernd67 Exp $
 */

#include "net_olsr.h"
#include "log.h"
#include "olsr.h"
#include "net_os.h"
#include "print_packet.h"
#include "link_set.h"
#include "lq_packet.h"

#include <stdlib.h>
#include <assert.h>

static olsr_bool disp_pack_out = OLSR_FALSE;


#ifdef WIN32
#define perror(x) WinSockPError(x)
void
WinSockPError(char *);
#endif


struct deny_address_entry
{
  union olsr_ip_addr        addr;
  struct deny_address_entry *next;
};


/* Packet transform functions */

struct ptf
{
  packet_transform_function function;
  struct ptf *next;
};

static struct ptf *ptf_list;

static struct deny_address_entry *deny_entries;

static const char * const deny_ipv4_defaults[] =
  {
    "0.0.0.0",
    "127.0.0.1",
    NULL
  };

static const char * const deny_ipv6_defaults[] =
  {
    "0::0",
    "0::1",
    NULL
  };

void
net_set_disp_pack_out(olsr_bool val)
{
  disp_pack_out = val;
}

void
init_net(void)
{
  const char * const *defaults = olsr_cnf->ip_version == AF_INET ? deny_ipv4_defaults : deny_ipv6_defaults;
  
  for (; *defaults != NULL; defaults++) {
    union olsr_ip_addr addr;
    if(inet_pton(olsr_cnf->ip_version, *defaults, &addr) <= 0){
      fprintf(stderr, "Error converting fixed IP %s for deny rule!!\n", *defaults);
      continue;
    }
    olsr_add_invalid_address(&addr);
  }
}

/**
 * Create a outputbuffer for the given interface. This
 * function will allocate the needed storage according 
 * to the MTU of the interface.
 *
 * @param ifp the interface to create a buffer for
 *
 * @return 0 on success, negative if a buffer already existed
 *  for the given interface
 */ 
int
net_add_buffer(struct interface *ifp)
{
  /* Can the interfaces MTU actually change? If not, we can elimiate
   * the "bufsize" field in "struct olsr_netbuf".
   */
  if (ifp->netbuf.bufsize != ifp->int_mtu && ifp->netbuf.buff != NULL) {
    free(ifp->netbuf.buff);
    ifp->netbuf.buff = NULL;
  }
  
  if (ifp->netbuf.buff == NULL) {
    ifp->netbuf.buff = olsr_malloc(ifp->int_mtu, "add_netbuff");
  }

  /* Fill struct */
  ifp->netbuf.bufsize = ifp->int_mtu;
  ifp->netbuf.maxsize = ifp->int_mtu - OLSR_HEADERSIZE;

  ifp->netbuf.pending = 0;
  ifp->netbuf.reserved = 0;

  return 0;
}

/**
 * Remove a outputbuffer. Frees the allocated memory.
 *
 * @param ifp the interface corresponding to the buffer
 * to remove
 *
 * @return 0 on success, negative if no buffer is found
 */
int
net_remove_buffer(struct interface *ifp)
{
  /* Flush pending data */
  if(ifp->netbuf.pending)
    net_output(ifp);

  free(ifp->netbuf.buff);
  ifp->netbuf.buff = NULL;

  return 0;
}


/**
 * Reserve space in a outputbuffer. This should only be needed
 * in very special cases. This will decrease the reported size
 * of the buffer so that there is always <i>size</i> bytes
 * of data available in the buffer. To add data in the reserved
 * area one must use the net_outbuffer_push_reserved function.
 *
 * @param ifp the interface corresponding to the buffer
 * to reserve space on
 * @param size the number of bytes to reserve
 *
 * @return 0 on success, negative if there was not enough
 *  bytes to reserve
 */
int
net_reserve_bufspace(struct interface *ifp, int size)
{
  if(size > ifp->netbuf.maxsize)
    return -1;
  
  ifp->netbuf.reserved = size;
  ifp->netbuf.maxsize -= size;
  
  return 0;
}

/**
 * Returns the number of bytes pending in the buffer. That
 * is the number of bytes added but not sent.
 *
 * @param ifp the interface corresponding to the buffer
 *
 * @return the number of bytes currently pending
 */
olsr_u16_t
net_output_pending(struct interface *ifp)
{
  return ifp->netbuf.pending;
}



/**
 * Add data to a buffer.
 *
 * @param ifp the interface corresponding to the buffer
 * @param data a pointer to the data to add
 * @param size the number of byte to copy from data
 *
 * @return -1 if no buffer was found, 0 if there was not 
 *  enough room in buffer or the number of bytes added on 
 *  success
 */
int
net_outbuffer_push(struct interface *ifp, const void *data, const olsr_u16_t size)
{
  if((ifp->netbuf.pending + size) > ifp->netbuf.maxsize)
    return 0;

  memcpy(&ifp->netbuf.buff[ifp->netbuf.pending + OLSR_HEADERSIZE], data, size);
  ifp->netbuf.pending += size;

  return size;
}


/**
 * Add data to the reserved part of a buffer
 *
 * @param ifp the interface corresponding to the buffer
 * @param data a pointer to the data to add
 * @param size the number of byte to copy from data
 *
 * @return -1 if no buffer was found, 0 if there was not 
 *  enough room in buffer or the number of bytes added on 
 *  success
 */
int
net_outbuffer_push_reserved(struct interface *ifp, const void *data, const olsr_u16_t size)
{
  if((ifp->netbuf.pending + size) > (ifp->netbuf.maxsize + ifp->netbuf.reserved))
    return 0;

  memcpy(&ifp->netbuf.buff[ifp->netbuf.pending + OLSR_HEADERSIZE], data, size);
  ifp->netbuf.pending += size;

  return size;
}

/**
 * Report the number of bytes currently available in the buffer
 * (not including possible reserved bytes)
 *
 * @param ifp the interface corresponding to the buffer
 *
 * @return the number of bytes available in the buffer or
 */
int
net_outbuffer_bytes_left(const struct interface *ifp)
{
  return ifp->netbuf.maxsize - ifp->netbuf.pending;
}


/**
 * Add a packet transform function. Theese are functions
 * called just prior to sending data in a buffer.
 *
 * @param f the function pointer
 *
 * @returns 1
 */
int
add_ptf(packet_transform_function f)
{

  struct ptf *new_ptf;

  new_ptf = olsr_malloc(sizeof(struct ptf), "Add PTF");

  new_ptf->next = ptf_list;
  new_ptf->function = f;

  ptf_list = new_ptf;

  return 1;
}

/**
 * Remove a packet transform function
 *
 * @param f the function pointer
 *
 * @returns 1 if a functionpointer was removed
 *  0 if not
 */
int
del_ptf(packet_transform_function f)
{
  struct ptf *tmp_ptf, *prev;

  tmp_ptf = ptf_list;
  prev = NULL;

  while(tmp_ptf)
    {
      if(tmp_ptf->function == f)
	{
	  /* Remove entry */
	  if(prev == NULL)
	      ptf_list = tmp_ptf->next;
	  else
	      prev->next = tmp_ptf->next;
          free(tmp_ptf);
	  return 1;
	}
      prev = tmp_ptf;
      tmp_ptf = tmp_ptf->next;
    }

  return 0;
}

/**
 *Sends a packet on a given interface.
 *
 *@param ifp the interface to send on.
 *
 *@return negative on error
 */
int
net_output(struct interface *ifp)
{
  struct sockaddr_in *sin;  
  struct sockaddr_in dst;
  struct sockaddr_in6 *sin6;  
  struct sockaddr_in6 dst6;
  struct ptf *tmp_ptf_list;
  union olsr_packet *outmsg;
  int retval;

  sin = NULL;
  sin6 = NULL;

  if(!ifp->netbuf.pending)
    return 0;

  ifp->netbuf.pending += OLSR_HEADERSIZE;

  retval = ifp->netbuf.pending;

  outmsg = (union olsr_packet *)ifp->netbuf.buff;
  /* Add the Packet seqno */
  outmsg->v4.olsr_seqno = htons(ifp->olsr_seqnum++);
  /* Set the packetlength */
  outmsg->v4.olsr_packlen = htons(ifp->netbuf.pending);

  if(olsr_cnf->ip_version == AF_INET)
    {
      /* IP version 4 */
      sin = (struct sockaddr_in *)&ifp->int_broadaddr;

      /* Copy sin */
      dst = *sin;
      sin = &dst;

      if (sin->sin_port == 0)
	sin->sin_port = htons(OLSRPORT);
    }
  else
    {
      /* IP version 6 */
      sin6 = (struct sockaddr_in6 *)&ifp->int6_multaddr;
      /* Copy sin */
      dst6 = *sin6;
      sin6 = &dst6;
    }

  /*
   *Call possible packet transform functions registered by plugins  
   */
  for (tmp_ptf_list = ptf_list; tmp_ptf_list != NULL; tmp_ptf_list = tmp_ptf_list->next)
    {
      tmp_ptf_list->function(ifp->netbuf.buff, &ifp->netbuf.pending);
    }

  /*
   *if the -dispout option was given
   *we print the content of the packets
   */
  if(disp_pack_out)
    print_olsr_serialized_packet(stdout, (union olsr_packet *)ifp->netbuf.buff, 
				 ifp->netbuf.pending, &ifp->ip_addr); 
  
  if(olsr_cnf->ip_version == AF_INET)
    {
      /* IP version 4 */
      if(olsr_sendto(ifp->olsr_socket, 
                     ifp->netbuf.buff, 
		     ifp->netbuf.pending, 
		     MSG_DONTROUTE, 
		     (struct sockaddr *)sin, 
		     sizeof (*sin))
	 < 0)
	{
	  perror("sendto(v4)");
	  olsr_syslog(OLSR_LOG_ERR, "OLSR: sendto IPv4 %m");
	  retval = -1;
	}
    }
  else
    {
      /* IP version 6 */
      if(olsr_sendto(ifp->olsr_socket, 
		     ifp->netbuf.buff,
		     ifp->netbuf.pending, 
		     MSG_DONTROUTE, 
		     (struct sockaddr *)sin6, 
		     sizeof (*sin6))
	 < 0)
	{
	  perror("sendto(v6)");
	  olsr_syslog(OLSR_LOG_ERR, "OLSR: sendto IPv6 %m");
	  fprintf(stderr, "Socket: %d interface: %d\n", ifp->olsr_socket, ifp->if_index);
	  fprintf(stderr, "To: %s (size: %d)\n", ip6_to_string(&sin6->sin6_addr), (int)sizeof(*sin6));
	  fprintf(stderr, "Outputsize: %d\n", ifp->netbuf.pending);
	  retval = -1;
	}
    }
  
  ifp->netbuf.pending = 0;

  // if we've just transmitted a TC message, let Dijkstra use the current
  // link qualities for the links to our neighbours

  olsr_update_dijkstra_link_qualities();
  lq_tc_pending = OLSR_FALSE;

  return retval;
}


/**
 * Create a IPv6 netmask based on a prefix length
 *
 * @param allocated address to build the netmask in
 * @param prefix the prefix length
 *
 * @returns 1 on success 0 on failure
 */
int
olsr_prefix_to_netmask(union olsr_ip_addr *adr, olsr_u16_t prefix)
{
  int p, i;

  if(adr == NULL)
    return 0;

  p = prefix;
  i = 0;

  memset(adr, 0, olsr_cnf->ipsize);

  for(;p > 0; p -= 8)
    {
      adr->v6.s6_addr[i] = (p < 8) ? 0xff ^ (0xff >> p) : 0xff;
      i++;
    }

#ifdef DEBUG
  OLSR_PRINTF(3, "Prefix %d = Netmask: %s\n", prefix, olsr_ip_to_string(adr));
#endif

  return 1;
}



/**
 * Calculate prefix length based on a netmask
 *
 * @param adr the address to use to calculate the prefix length
 *
 * @return the prefix length
 */
olsr_u16_t
olsr_netmask_to_prefix(const union olsr_ip_addr *adr)
{
  olsr_u16_t prefix = 0;
  unsigned int i;

  prefix = 0;

  for(i = 0; i < olsr_cnf->ipsize; i++)
    {
      if(adr->v6.s6_addr[i] == 0xff)
	{
	  prefix += 8;
	}
      else
	{
          int tmp;
	  for(tmp = adr->v6.s6_addr[i];
	      tmp > 0;
	      tmp = (tmp << 1) & 0xff)
	    prefix++;
	}
    }

#ifdef DEBUG
  OLSR_PRINTF(3, "Netmask: %s = Prefix %d\n", olsr_ip_to_string(adr), prefix);
#endif

  return prefix;
}


/**
 *Converts a sockaddr struct to a string representing
 *the IP address from the sockaddr struct
 *
 *<b>NON REENTRANT!!!!</b>
 *
 *@param address_to_convert the sockaddr struct to "convert"
 *@return a char pointer to the string containing the IP
 */
char *
sockaddr_to_string(const struct sockaddr *address_to_convert)
{
  const struct sockaddr_in *address = (const struct sockaddr_in *)address_to_convert; 
  return inet_ntoa(address->sin_addr);
  
}


/**
 *Converts the 32bit olsr_u32_t datatype to
 *a char array.
 *
 *<b>NON REENTRANT!!!!</b>
 *
 *@param address the olsr_u32_t to "convert"
 *@return a char pointer to the string containing the IP
 */

const char *
ip_to_string(const olsr_u32_t *address)
{
  struct in_addr in;
  in.s_addr=*address;
  return(inet_ntoa(in));
  
}

/**
 *Converts the 32bit olsr_u32_t datatype to
 *a char array.
 *
 *<b>NON REENTRANT</b>
 *
 *@param addr6 the address to "convert"
 *@return a char pointer to the string containing the IP
 */

const char *
ip6_to_string(const struct in6_addr *addr6)
{
  static char ipv6_buf[INET6_ADDRSTRLEN]; /* for address coversion */
  return inet_ntop(AF_INET6, addr6, ipv6_buf, sizeof(ipv6_buf));
}


const char *
olsr_ip_to_string(const union olsr_ip_addr *addr)
{
  static int idx = 0;
  static char buff[4][INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN];
  const char *ret;

  if (!addr) {
      return "null";
  }
  
  if(olsr_cnf->ip_version == AF_INET)
    {
#if 0
      struct in_addr in;
      in.s_addr = addr->v4;
      ret = inet_ntop(AF_INET, &in, buff[idx], sizeof(buff[idx]));
#else
      ret = inet_ntop(AF_INET, &addr->v4, buff[idx], sizeof(buff[idx]));
#endif
    }
  else
    {
      /* IPv6 */
      ret = inet_ntop(AF_INET6, &addr->v6, buff[idx], sizeof(buff[idx]));
    }
  idx = (idx + 1) & 3;

  return ret;
}


void
olsr_add_invalid_address(const union olsr_ip_addr *adr)
{
  struct deny_address_entry *new_entry;

  new_entry = olsr_malloc(sizeof(struct deny_address_entry), "Add deny address");

  new_entry->next = deny_entries;
  COPY_IP(&new_entry->addr, adr);

  deny_entries = new_entry;

  OLSR_PRINTF(1, "Added %s to IP deny set\n", olsr_ip_to_string(&new_entry->addr));
  return;
}

/**
 *Converts the 32bit olsr_u32_t datatype to
 *a char array.
 *
 *<b>NON REENTRANT</b>
 *
 *@param addr6 the address to "convert"
 *@return a char pointer to the string containing the IP
 */
olsr_bool
olsr_validate_address(const union olsr_ip_addr *adr)
{
  const struct deny_address_entry *deny_entry = deny_entries;

  while(deny_entry)
    {
      if(COMP_IP(adr, &deny_entry->addr))
	{
	  OLSR_PRINTF(1, "Validation of address %s failed!\n",
		      olsr_ip_to_string(adr));
	  return OLSR_FALSE;
	}

      deny_entry = deny_entry->next;
    }

  return OLSR_TRUE;
}

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


syntax highlighted by Code2HTML, v. 0.9.1