/*
 *	Simple IPv6 network layer implementation.
 *	Based and/or adapted from the IPv4 network layer in net.c
 *
 *	Copyright 2006 - 2012   Hermin Anggawijaya - Allied Telesis Labs NZ
 *	(See License)
 *
 *
 * History
 *	8/Nov/2006	  created
 *
 */

#ifndef __NET6_H__
#define __NET6_H__

#define IP_SET_HTONL(xxaddr, yyval) do { \
   ((u_char *)(xxaddr))[0] = (u_char)((((u_int32_t)(yyval)) >> 24) & 0xff); \
   ((u_char *)(xxaddr))[1] = (u_char)((((u_int32_t)(yyval)) >> 16) & 0xff); \
   ((u_char *)(xxaddr))[2] = (u_char)((((u_int32_t)(yyval)) >>  8) & 0xff); \
   ((u_char *)(xxaddr))[3] = (u_char) (((u_int32_t)(yyval)) & 0xff); \
} while(0)

#define IP6_ADDR_MAXSTRLEN	sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")

typedef union	ip6addr_t
{
	__u8		u6_addr8[16];
	__u16		u6_addr16[8];
	__u32		u6_addr32[4];

} IP6addr_t;


#define INT16SZ      sizeof(u_int16_t)
#define IN6ADDRSZ    16
#define INETHADDRSZ	 sizeof(NetOurEther)

#define IPV6_ADDRSCOPE_LINK 			0x02

/*
 *	Internet Protocol V6 (IPv6) header.
 *	BEWARE, it is incorrect. The first 4 bits of flow_lbl
 *	are glued to priority now, forming "class".
 */

typedef struct ipv6hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	priority:4,
				version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
				priority:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8			flow_lbl[3];
	__u16			payload_len;
	__u8			nexthdr;
	__u8			hop_limit;
	IP6addr_t	saddr;
	IP6addr_t	daddr;
} IP6_t;

#define IP6_HDR_SIZE_NO_UDP	(sizeof (IP6_t) - 8)
#define IP6_HDR_SIZE		(sizeof (IP6_t))

typedef struct udp_hdr
{
	__u16		udp_src;	/* UDP source port		*/
	__u16		udp_dst;	/* UDP destination port		*/
	__u16		udp_len;	/* Length of UDP packet		*/
	__u16		udp_xsum;	/* Checksum			*/
} UDP_t;

#define IP6_UDPHDR_SIZE		(sizeof (UDP_t))


/*
 *	ndisc options
 */

enum
{
	__ND_OPT_PREFIX_INFO_END	= 0,
	ND_OPT_SOURCE_LL_ADDR			= 1, 
	ND_OPT_TARGET_LL_ADDR			= 2, 
	ND_OPT_PREFIX_INFO				= 3, 
	ND_OPT_REDIRECT_HDR				= 4, 
	ND_OPT_MTU								= 5, 
	__ND_OPT_MAX
};


#define IPPROTO_ICMPV6				58	/* ICMPv6			*/
#define IPV6_NDISC_HOPLIMIT		255	/* hop limit for neighbour discovery packets */
#define NDISC_TIMEOUT					5
#define NDISC_TIMEOUT_COUNT		3

/* ICMP types for echo and neighbour discovery */
#define IPV6_ICMP_ECHO_REQUEST							128
#define IPV6_ICMP_ECHO_REPLY								129
#define IPV6_NDISC_ROUTER_SOLICITATION			133
#define IPV6_NDISC_ROUTER_ADVERTISEMENT			134
#define IPV6_NDISC_NEIGHBOUR_SOLICITATION		135
#define IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT	136
#define IPV6_NDISC_REDIRECT									137

#define IP6_NDISC_OPT_SPACE(len) (((len)+2+7)&~7)

typedef struct icmp6hdr {

	__u8		icmp6_type;
	__u8		icmp6_code;
	__u16		icmp6_cksum;


	union {
		__u32			un_data32[1];
		__u16			un_data16[2];
		__u8			un_data8[4];

		struct icmpv6_echo {
			__u16		identifier;
			__u16		sequence;
		} u_echo;

		struct icmpv6_nd_advt {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    	__u32		reserved:5,
       				override:1,
       				solicited:1,
       				router:1,
							reserved2:24;
#elif defined(__BIG_ENDIAN_BITFIELD)
      __u32		router:1,
							solicited:1,
              override:1,
              reserved:29;
#else
#error	"Please fix <asm/byteorder.h>"
#endif						
    } u_nd_advt;

    struct icmpv6_nd_ra {
			__u8		hop_limit;
#if defined(__LITTLE_ENDIAN_BITFIELD)
			__u8		reserved:6,
							other:1,
							managed:1;

#elif defined(__BIG_ENDIAN_BITFIELD)
			__u8		managed:1,
							other:1,
							reserved:6;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
			__u16		rt_lifetime;
   	} u_nd_ra;
	} icmp6_dataun;
#define icmp6_identifier	icmp6_dataun.u_echo.identifier
#define icmp6_sequence		icmp6_dataun.u_echo.sequence
#define icmp6_pointer		icmp6_dataun.un_data32[0]
#define icmp6_mtu		icmp6_dataun.un_data32[0]
#define icmp6_unused		icmp6_dataun.un_data32[0]
#define icmp6_maxdelay		icmp6_dataun.un_data16[0]
#define icmp6_router		icmp6_dataun.u_nd_advt.router
#define icmp6_solicited		icmp6_dataun.u_nd_advt.solicited
#define icmp6_override		icmp6_dataun.u_nd_advt.override
#define icmp6_ndiscreserved	icmp6_dataun.u_nd_advt.reserved
#define icmp6_hop_limit		icmp6_dataun.u_nd_ra.hop_limit
#define icmp6_addrconf_managed	icmp6_dataun.u_nd_ra.managed
#define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
#define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
} ICMP6_t;


typedef struct nd_msg
{
	ICMP6_t		icmph;
  IP6addr_t	target;
	__u8			opt[0];
} NDISC_ND_t;

typedef struct rs_msg
{
	ICMP6_t	icmph;
	__u8		opt[0];
} NDISC_RS_t;

typedef struct ra_msg
{
	ICMP6_t		icmph;
	__u32			reachable_time;
	__u32			retrans_timer;
} NDISC_RA_t;

typedef struct echo_msg
{
	ICMP6_t		icmph;
  __u16			id;
  __u16			sequence;
} PING6_t;

struct nd_opt_hdr
{
	__u8		nd_opt_type;
	__u8		nd_opt_len;
} __attribute__((__packed__));

typedef struct nd_op_hdr NDISC_Opt_t;


extern IP6addr_t	const NetNullAddrIP6;		/* NULL IPv6 address	*/
extern IP6addr_t	NetOurGatewayIP6;	/* Our gateways IPv6 address	*/
extern IP6addr_t	NetOurIP6;				/* Our IPv6 addr (0 = unknown)		*/
extern u_int32_t	NetPrefixLength;	/* Our prefixlength (0 = unknown)*/
extern IP6addr_t	NetServerIP6;			/* set server IPv6 addr (0 = unknown)		*/

#if (CONFIG_COMMANDS & CFG_CMD_PING)
extern IP6addr_t	NetPingIP6;		/* the ipv6 address to ping 		*/
#endif

extern IP6addr_t	NetNDSolPacketIP6;	/* IPv6 destination address of packet waiting for ND */
extern IP6addr_t	NetNDRepPacketIP6;	/* IPv6 address we are expecting ND advert from */
extern uchar			*NetNDPacketMAC;		/* MAC destination address of packet waiting for ND */
extern uchar			*NetNDTxPacket;			/* pointer to packet waiting to be transmitted after ND is resolved */
extern int				NetNDTxPacketSize;	/* size of packet waiting to be transmitted */
extern ulong			NetNDTimerStart;		/* the timer for ND resolution */
extern int				NetNDTry;						/* the number of requests we have sent so far */


/* Set IPv6 header */
extern int ip6_add_hdr(volatile uchar *xip, IP6addr_t *src, IP6addr_t *dest, int nextheader,
	int hoplimit, int payload_len);

/* Transmit UDP packet using IPv6, performing neighbour discovery if needed */
extern int NetSendUDPPacket6 (uchar *ether, IP6addr_t *dest, int dport, int sport, int len);

/* Print an IPv6 address on the console */
extern void	print_IP6addr (IP6addr_t *);

/* Print an IPv6 address on the stdout */
extern void printf_IP6addr (IP6addr_t *x);

/* return IPv6 *in network byteorder* */
static inline void NetReadIP6(void *from, IP6addr_t *ip6)
{
	memcpy((void*)ip6, from, sizeof(IP6addr_t));
	return;
}

/* write IPv6 *in network byteorder* */
static inline void NetWriteIP6(void *to, IP6addr_t *ip6)
{
	memcpy(to, (void*)ip6, sizeof(IP6addr_t));
}

static inline void NetCopyIP6(void *to, void *from)
{
	memcpy(to, from, sizeof(IP6addr_t));
}

static inline int NetCompareIP6(const void *to, const void *from)
{
	return memcmp(to, from, sizeof(IP6addr_t));
}

/* Convert an IPv6 address to a string */
extern char *ip6_to_string(const u_int8_t * src, int8_t * dst, size_t size);

/* Convert a string to ipv6 address */
int string_to_ip6(const char *strptr, void *addrptr);

/* read an IP address from a environment variable */
extern int getenv_IP6addr (char *var, IP6addr_t *ip6addr);

/* check if neighbour is in the same subnet as us */
extern int ip6_addr_in_subnet( IP6addr_t *our_addr, IP6addr_t *neigh_addr, __u32 prefix_length );

/* send a neighbour discovery solicitation message */
extern void ip6_NDISC_Request (void);

/* call back routine when ND timer has gone off */
extern void ip6_NDISC_TimeoutCheck (void);

/* initialises the ND data */
extern void ip6_NDISC_init (void);

/* sends an IPv6 echo request to a host */
extern int PingSend6(void);

/* starts a Ping6 process */
extern void PingStart6 (void);

/* handler for incoming IPv6 echo packet */
extern void NetIP6PacketHandler ( IP6_t *ip6, int len );

/* prints out debugging messages */
extern void debug_printf (const char *fmt, ...);



#endif /* __NET6_H__ */

