/*
     This file is part of GNUnet.
     (C) 2010, 2011, 2012 Christian Grothoff

     GNUnet is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published
     by the Free Software Foundation; either version 3, or (at your
     option) any later version.

     GNUnet is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with GNUnet; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     Boston, MA 02111-1307, USA.
*/

/**
 * @file include/gnunet_tun_lib.h
 * @brief standard TCP/IP network structs and IP checksum calculations for TUN interaction
 * @author Philipp Toelke
 * @author Christian Grothoff
 */
#ifndef GNUNET_TUN_LIB_H
#define GNUNET_TUN_LIB_H

#include "gnunet_util_lib.h"


/* see http://www.iana.org/assignments/ethernet-numbers */
#ifndef ETH_P_IPV4
/**
 * Number for IPv4
 */
#define ETH_P_IPV4 0x0800
#endif

#ifndef ETH_P_IPV6
/**
 * Number for IPv6
 */
#define ETH_P_IPV6 0x86DD
#endif


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Header from Linux TUN interface.
 */ 
struct GNUNET_TUN_Layer2PacketHeader
{
  /**
   * Some flags (unused).
   */ 
  uint16_t flags GNUNET_PACKED;

  /**
   * Here we get an ETH_P_-number.
   */
  uint16_t proto GNUNET_PACKED;
};


/**
 * Standard IPv4 header.
 */
struct GNUNET_TUN_IPv4Header
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
  unsigned int header_length:4 GNUNET_PACKED;
  unsigned int version:4 GNUNET_PACKED;
#elif __BYTE_ORDER == __BIG_ENDIAN
  unsigned int version:4 GNUNET_PACKED;
  unsigned int header_length:4 GNUNET_PACKED;
#else
  #error byteorder undefined
#endif
  uint8_t diff_serv;

  /**
   * Length of the packet, including this header.
   */
  uint16_t total_length GNUNET_PACKED;
  
  /**
   * Unique random ID for matching up fragments.
   */
  uint16_t identification GNUNET_PACKED;

  unsigned int flags:3 GNUNET_PACKED;

  unsigned int fragmentation_offset:13 GNUNET_PACKED;

  /**
   * How many more hops can this packet be forwarded?
   */
  uint8_t ttl;

  /**
   * L4-protocol, for example, IPPROTO_UDP or IPPROTO_TCP.
   */
  uint8_t protocol;

  /**
   * Checksum.
   */
  uint16_t checksum GNUNET_PACKED;

  /**
   * Origin of the packet.
   */ 
  struct in_addr source_address GNUNET_PACKED;

  /**
   * Destination of the packet.
   */ 
  struct in_addr destination_address GNUNET_PACKED;
} GNUNET_GCC_STRUCT_LAYOUT;


/**
 * Standard IPv6 header.
 */
struct GNUNET_TUN_IPv6Header
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
  unsigned int traffic_class_h:4 GNUNET_PACKED;
  unsigned int version:4 GNUNET_PACKED;
  unsigned int traffic_class_l:4 GNUNET_PACKED;
  unsigned int flow_label:20 GNUNET_PACKED;
#elif __BYTE_ORDER == __BIG_ENDIAN
  unsigned int version:4 GNUNET_PACKED;
  unsigned int traffic_class:8 GNUNET_PACKED;
  unsigned int flow_label:20 GNUNET_PACKED;
#else
  #error byteorder undefined
#endif
  /**
   * Length of the payload, excluding this header.
   */
  uint16_t payload_length GNUNET_PACKED;

  /**
   * For example, IPPROTO_UDP or IPPROTO_TCP.
   */
  uint8_t next_header;

  /**
   * How many more hops can this packet be forwarded?
   */
  uint8_t hop_limit;

  /**
   * Origin of the packet.
   */ 
  struct in6_addr source_address GNUNET_PACKED;

  /**
   * Destination of the packet.
   */ 
  struct in6_addr destination_address GNUNET_PACKED;
} GNUNET_GCC_STRUCT_LAYOUT;


/**
 * TCP packet header.
 */
struct GNUNET_TUN_TcpHeader
{
  uint16_t source_port GNUNET_PACKED;
  uint16_t destination_port GNUNET_PACKED;

  /**
   * Sequence number.
   */
  uint32_t seq GNUNET_PACKED;

  /**
   * Acknowledgement number.
   */
  uint32_t ack GNUNET_PACKED;
#if __BYTE_ORDER == __LITTLE_ENDIAN
  /**
   * Reserved.  Must be zero.
   */
  unsigned int reserved : 4 GNUNET_PACKED;
  /**
   * Number of 32-bit words in TCP header.
   */
  unsigned int off : 4 GNUNET_PACKED;
#elif __BYTE_ORDER == __BIG_ENDIAN
  /**
   * Number of 32-bit words in TCP header.
   */
  unsigned int off : 4 GNUNET_PACKED;
  /**
   * Reserved.  Must be zero.
   */
  unsigned int reserved : 4 GNUNET_PACKED;
#else
  #error byteorder undefined
#endif        

  /**
   * Flags (SYN, FIN, ACK, etc.)
   */
  uint8_t flags;

  /**
   * Window size.
   */
  uint16_t window_size GNUNET_PACKED;

  /**
   * Checksum.
   */
  uint16_t crc GNUNET_PACKED;

  /**
   * Urgent pointer.
   */
  uint16_t urgent_pointer GNUNET_PACKED;
} GNUNET_GCC_STRUCT_LAYOUT;


/**
 * UDP packet header.
 */
struct GNUNET_TUN_UdpHeader
{
  uint16_t source_port GNUNET_PACKED;
  uint16_t destination_port GNUNET_PACKED;
  uint16_t len GNUNET_PACKED;
  uint16_t crc GNUNET_PACKED;
};


/**
 * DNS header.
 */
struct GNUNET_TUN_DnsHeader
{
  uint16_t id GNUNET_PACKED;
  uint16_t flags GNUNET_PACKED;
  uint16_t qdcount GNUNET_PACKED;
  uint16_t ancount GNUNET_PACKED;
  uint16_t nscount GNUNET_PACKED;
  uint16_t arcount GNUNET_PACKED;
};

#define	GNUNET_TUN_ICMPTYPE_ECHO_REPLY 0
#define	GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE 3
#define	GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH 4
#define	GNUNET_TUN_ICMPTYPE_REDIRECT_MESSAGE 5
#define	GNUNET_TUN_ICMPTYPE_ECHO_REQUEST 8
#define	GNUNET_TUN_ICMPTYPE_ROUTER_ADVERTISEMENT 9
#define	GNUNET_TUN_ICMPTYPE_ROUTER_SOLICITATION 10
#define	GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED 11

#define	GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE 1
#define	GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG 2
#define	GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED 3
#define	GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM 4
#define	GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST 128
#define	GNUNET_TUN_ICMPTYPE6_ECHO_REPLY 129


/**
 * ICMP header.
 */
struct GNUNET_TUN_IcmpHeader {
  uint8_t type;		
  uint8_t code;		 
  uint16_t crc GNUNET_PACKED;

  union {
    /**
     * ICMP Echo (request/reply) 
     */
    struct {
      uint16_t	identifier GNUNET_PACKED;
      uint16_t	sequence_number GNUNET_PACKED;
    } echo;

    /**
     * ICMP Destination Unreachable (RFC 1191) 
     */
    struct ih_pmtu {
      uint16_t empty GNUNET_PACKED;
      uint16_t next_hop_mtu GNUNET_PACKED;
      /* followed by original IP header + first 8 bytes of original IP datagram */
    } destination_unreachable;

    /**
     * ICMP Redirect 
     */	
    struct in_addr redirect_gateway_address GNUNET_PACKED;	

    /**
     * MTU for packets that are too big (IPv6).
     */
    uint32_t packet_too_big_mtu GNUNET_PACKED;

  } quench;

};


GNUNET_NETWORK_STRUCT_END


/**
 * Initialize an IPv4 header.
 *
 * @param ip header to initialize
 * @param protocol protocol to use (i.e. IPPROTO_UDP)
 * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
 * @param src source IP address to use
 * @param dst destination IP address to use
 */
void
GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
				   uint8_t protocol,
				   uint16_t payload_length,
				   const struct in_addr *src,
				   const struct in_addr *dst);


/**
 * Initialize an IPv6 header.
 *
 * @param ip header to initialize
 * @param protocol protocol to use (i.e. IPPROTO_UDP)
 * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
 * @param src source IP address to use
 * @param dst destination IP address to use
 */
void
GNUNET_TUN_initialize_ipv6_header (struct GNUNET_TUN_IPv6Header *ip,
				   uint8_t protocol,
				   uint16_t payload_length,
				   const struct in6_addr *src,
				   const struct in6_addr *dst);

/**
 * Calculate IPv4 TCP checksum.
 *
 * @param ip ipv4 header fully initialized
 * @param tcp TCP header (initialized except for CRC)
 * @param payload the TCP payload
 * @param payload_length number of bytes of TCP payload
 */
void
GNUNET_TUN_calculate_tcp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
				    struct GNUNET_TUN_TcpHeader *tcp,
				    const void *payload,
				    uint16_t payload_length);

/**
 * Calculate IPv6 TCP checksum.
 *
 * @param ip ipv6 header fully initialized
 * @param tcp TCP header (initialized except for CRC)
 * @param payload the TCP payload
 * @param payload_length number of bytes of TCP payload
 */
void
GNUNET_TUN_calculate_tcp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
				    struct GNUNET_TUN_TcpHeader *tcp,
				    const void *payload,
				    uint16_t payload_length);

/**
 * Calculate IPv4 UDP checksum.
 *
 * @param ip ipv4 header fully initialized
 * @param udp UDP header (initialized except for CRC)
 * @param payload the UDP payload
 * @param payload_length number of bytes of UDP payload
 */
void
GNUNET_TUN_calculate_udp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
				    struct GNUNET_TUN_UdpHeader *udp,
				    const void *payload,
				    uint16_t payload_length);


/**
 * Calculate IPv6 UDP checksum.
 *
 * @param ip ipv6 header fully initialized
 * @param udp UDP header (initialized except for CRC)
 * @param payload the UDP payload
 * @param payload_length number of bytes of UDP payload
 */
void
GNUNET_TUN_calculate_udp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
				    struct GNUNET_TUN_UdpHeader *udp,
				    const void *payload,
				    uint16_t payload_length);


/**
 * Calculate ICMP checksum.
 *
 * @param icmp IMCP header (initialized except for CRC)
 * @param payload the ICMP payload
 * @param payload_length number of bytes of ICMP payload
 */
void
GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
				    const void *payload,
				    uint16_t payload_length);


#endif
