/*
 * Copyright (c) 2008, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef _LIBSA_NET_TYPES_H_
#define	_LIBSA_NET_TYPES_H_

#if !defined(NTOHL)
#include <netinet/in.h>
#endif /* NTOHL */

/*
 * Type definitions for network order fields in protocol packets.
 * The access functions below do gets and puts on these structures.
 */
typedef unsigned char net8_t;		/* direct use and assignment allowed */

/*
 * Aligned network order types.
 */
typedef struct {
	u_int16_t	net_data;
} net16_t;

typedef struct {
	u_int32_t	net_data;
} net32_t;

/*
 * The 64-bit type only requires 32-bit alignment.
 */
typedef struct {
	u_int32_t	net_data[2];	/* most significant word first */
} net64_t;

/*
 * 24-bit type.  Byte aligned, in spite of the name.
 */
typedef struct {
	unsigned char	net_data[3];
} net24_t;

/*
 * 48-bit type.  Byte aligned.
 */
typedef struct {
	unsigned char	net_data[6];
} net48_t;

/*
 * Unaligned network order types.
 * Any of these structures can be byte aligned.  No padding is implied.
 */
typedef struct {
	unsigned char	net_data[2];
} ua_net16_t;

typedef struct {
	unsigned char	net_data[4];
} ua_net32_t;

typedef struct {
	unsigned char	net_data[8];
} ua_net64_t;

/*
 * Accessor functions.
 */

/**
 * net8_get(net) - fetch from a network-order 8-bit field.
 *
 * @param net pointer to network-order 8-bit data.
 * @return the host-order value.
 */
static inline u_int8_t net8_get(const net8_t * net)
{
	return *net;
}

/**
 * net8_put(net, val) - store to a network-order 8-bit field.
 *
 * @param net pointer to network-order 8-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net8_put(net8_t * net, u_int8_t val)
{
	*net = val;
}

/**
 * net16_get(net) - fetch from a network-order 16-bit field.
 *
 * @param net pointer to type net16_t, network-order 16-bit data.
 * @return the host-order value.
 */
static inline u_int16_t net16_get(const net16_t * net)
{
	return ntohs(net->net_data);
}

/**
 * net16_put(net, val) - store to a network-order 16-bit field.
 *
 * @param net pointer to a net16_t, network-order 16-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net16_put(net16_t * net, u_int16_t val)
{
	net->net_data = htons(val);
}

/**
 * ua_net16_get(net) - fetch from an unaligned network-order 16-bit field.
 *
 * @param net pointer to type ua_net16_t, unaligned, network-order 16-bit data.
 * @return the host-order value.
 */
static inline u_int16_t ua_net16_get(const ua_net16_t * net)
{
	return (net->net_data[0] << 8) | net->net_data[1];
}

/**
 * ua_net16_put(net, val) - store to a network-order 16-bit field.
 *
 * @param net pointer to a ua_net16_t, network-order 16-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void ua_net16_put(ua_net16_t * net, u_int16_t val)
{
	net->net_data[0] = (u_int8_t)((val >> 8) & 0xFF);
	net->net_data[1] = (u_int8_t)(val & 0xFF);
}

/**
 * net24_get(net) - fetch from a network-order 24-bit field.
 *
 * @param net pointer to type net24_t, network-order 24-bit data.
 * @return the host-order value.
 */
static inline u_int32_t net24_get(const net24_t * net)
{
	return (net->net_data[0] << 16) |
		(net->net_data[1] << 8) | net->net_data[2];
}

/**
 * net24_put(net, val) - store to a network-order 24-bit field.
 *
 * @param net pointer to a net24_t, network-order 24-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net24_put(net24_t * net, u_int32_t val)
{
	net->net_data[0] = (u_int8_t)((val >> 16) & 0xFF);
	net->net_data[1] = (u_int8_t)((val >> 8) & 0xFF);
	net->net_data[2] = (u_int8_t)(val & 0xFF);
}

/**
 * net32_get(net) - fetch from a network-order 32-bit field.
 *
 * @param net pointer to type net32_t, network-order 32-bit data.
 * @return the host-order value.
 */
static inline u_int32_t net32_get(const net32_t * net)
{
	return ntohl(net->net_data);
}

/**
 * net32_put(net, val) - store to a network-order 32-bit field.
 *
 * @param net pointer to a net32_t, network-order 32-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net32_put(net32_t * net, u_int32_t val)
{
	net->net_data = htonl(val);
}

/**
 * ua_net32_get(net) - fetch from an unaligned network-order 32-bit field.
 *
 * @param net pointer to type ua_net32_t, unaligned, network-order 32-bit data.
 * @return the host-order value.
 */
static inline u_int32_t ua_net32_get(const ua_net32_t * net)
{
	return (net->net_data[0] << 24) | (net->net_data[1] << 16) |
		(net->net_data[2] << 8) | net->net_data[3];
}

/**
 * ua_net32_put(net, val) - store to a network-order 32-bit field.
 *
 * @param net pointer to a ua_net32_t, network-order 32-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void ua_net32_put(ua_net32_t * net, u_int32_t val)
{
	net->net_data[0] = (u_int8_t)((val >> 24) & 0xFF);
	net->net_data[1] = (u_int8_t)((val >> 16) & 0xFF);
	net->net_data[2] = (u_int8_t)((val >> 8) & 0xFF);
	net->net_data[3] = (u_int8_t)(val & 0xFF);
}

/**
 * net48_get(net) - fetch from a network-order 48-bit field.
 *
 * @param net pointer to type net48_t, network-order 48-bit data.
 * @return the host-order value.
 */
static inline u_int64_t net48_get(const net48_t * net)
{
	return ((u_int64_t) net->net_data[0] << 40) |
		((u_int64_t) net->net_data[1] << 32) |
		((u_int64_t) net->net_data[2] << 24) |
		((u_int64_t) net->net_data[3] << 16) |
		((u_int64_t) net->net_data[4] << 8) |
		(u_int64_t) net->net_data[5];
}

/**
 * net48_put(net, val) - store to a network-order 48-bit field.
 *
 * @param net pointer to a net48_t, network-order 48-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net48_put(net48_t * net, u_int64_t val)
{
	net->net_data[0] = (u_int8_t)((val >> 40) & 0xFF);
	net->net_data[1] = (u_int8_t)((val >> 32) & 0xFF);
	net->net_data[2] = (u_int8_t)((val >> 24) & 0xFF);
	net->net_data[3] = (u_int8_t)((val >> 16) & 0xFF);
	net->net_data[4] = (u_int8_t)((val >> 8) & 0xFF);
	net->net_data[5] = (u_int8_t)(val & 0xFF);
}

/**
 * net64_get(net) - fetch from a network-order 64-bit field.
 *
 * @param net pointer to type net64_t, network-order 64-bit data.
 * @return the host-order value.
 */
static inline u_int64_t net64_get(const net64_t * net)
{
	return ((u_int64_t) ntohl(net->net_data[0]) << 32) |
		ntohl(net->net_data[1]);
}

/**
 * net64_put(net, val) - store to a network-order 64-bit field.
 *
 * @param net pointer to a net64_t, network-order 64-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void net64_put(net64_t * net, u_int64_t val)
{
	net->net_data[0] = (u_int32_t)htonl(val >> 32);
	net->net_data[1] = (u_int32_t)htonl((u_int32_t) val);
}

/**
 * ua_net64_get(net) - fetch from an unaligned network-order 64-bit field.
 *
 * @param net pointer to type ua_net64_t, unaligned, network-order 64-bit data.
 * @return the host-order value.
 */
static inline u_int64_t ua_net64_get(const ua_net64_t * net)
{
	return ((u_int64_t) net->net_data[0] << 56) |
		((u_int64_t) net->net_data[1] << 48) |
		((u_int64_t) net->net_data[2] << 40) |
		((u_int64_t) net->net_data[3] << 32) |
		((u_int64_t) net->net_data[4] << 24) |
		((u_int64_t) net->net_data[5] << 16) |
		((u_int64_t) net->net_data[6] << 8) |
		(u_int64_t) net->net_data[7];
}

/**
 * ua_net64_put(net, val) - store to a network-order 64-bit field.
 *
 * @param net pointer to a ua_net64_t, network-order 64-bit data.
 * @param val host-order value to be stored at net.
 */
static inline void ua_net64_put(ua_net64_t * net, u_int64_t val)
{
	net->net_data[0] = (u_int8_t)((val >> 56) & 0xFF);
	net->net_data[1] = (u_int8_t)((val >> 48) & 0xFF);
	net->net_data[2] = (u_int8_t)((val >> 40) & 0xFF);
	net->net_data[3] = (u_int8_t)((val >> 32) & 0xFF);
	net->net_data[4] = (u_int8_t)((val >> 24) & 0xFF);
	net->net_data[5] = (u_int8_t)((val >> 16) & 0xFF);
	net->net_data[6] = (u_int8_t)((val >> 8) & 0xFF);
	net->net_data[7] = (u_int8_t)(val & 0xFF);
}

/*
 * Compile-time initializers for the network-order type structures.
 * Note that the upper byte of these values is not masked so the
 * compiler will catch initializers that don't fit in the field.
 */

/**
 * NET8_INIT(_val) - initialize a net8_t type.
 *
 * @param _val 8-bit value.
 * @return net8_t network-order value.
 */
#define	NET8_INIT(_val)     (_val)

/**
 * NET24_INIT(_val) - initialize a net24_t type.
 *
 * @param _val host-order value.
 * @return net24_t network-order value.
 */
#define	NET24_INIT(_val)    { {				    \
				((_val) >> 16),		    \
				((_val) >> 8) & 0xff,	    \
				((_val) >> 0) & 0xff	    \
			    } }

/**
 * NET48_INIT(_val) - initialize a net48_t type.
 *
 * @param _val host-order value.
 * @return net48_t network-order value.
 */
#define	NET48_INIT(_val)    { {				    \
				((_val) >> 40),		    \
				((_val) >> 32) & 0xff,	    \
				((_val) >> 24) & 0xff,	    \
				((_val) >> 16) & 0xff,	    \
				((_val) >> 8) & 0xff,	    \
				((_val) >> 0) & 0xff	    \
			    } }

/**
 * NET16_INIT(_val) - initialize a net16_t type.
 *
 * @param _val host-order value.
 * @return net16_t network-order value.
 */
#define	NET16_INIT(_val)    {	htons(_val) }

/**
 * UA_NET16_INIT(_val) - initialize an unaligned 16-bit type.
 *
 * @param _val host-order value.
 * @return ua_net24_t network-order value.
 */
#define	UA_NET16_INIT(_val) { {				    \
				((_val) >> 8),		    \
				((_val) >> 0) & 0xff	    \
			    } }

/**
 * NET32_INIT(_val) - initialize a 32-bit type.
 *
 * @param _val host-order value.
 * @return net32_t network-order value.
 */
#define	NET32_INIT(_val)    {	htonl(_val) }

/**
 * UA_NET32_INIT(_val) - initialize an unaligned 32-bit type.
 *
 * @param _val host-order value.
 * @return ua_net32_t network-order value.
 */
#define	UA_NET32_INIT(_val) { {				    \
				((_val) >> 24),		    \
				((_val) >> 16) & 0xff,	    \
				((_val) >> 8) & 0xff,	    \
				((_val) >> 0) & 0xff	    \
			    } }

/**
 * UA_NET48_INIT(_val) - initialize an unaligned 48-bit type.
 *
 * @param _val host-order value.
 * @return ua_net48_t network-order value.
 */
#define	UA_NET48_INIT(_val) { {				    \
				((_val) >> 40),		    \
				((_val) >> 32) & 0xff,	    \
				((_val) >> 24) & 0xff,	    \
				((_val) >> 16) & 0xff,	    \
				((_val) >> 8) & 0xff,	    \
				((_val) >> 0) & 0xff	    \
			    } }

/**
 * NET64_INIT(_val) - initialize an unaligned 64-bit type.
 *
 * @param _val host-order value.
 * @return ua_net64_t network-order value.
 */
#define	NET64_INIT(_val)    { {				    \
				htonl((_val) >> 32),	    \
				htonl((_val) & 0xffffffff)  \
			    } }

/**
 * UA_NET64_INIT(_val) - initialize a 64-bit type.
 *
 * @param _val host-order value.
 * @return net64_t network-order value.
 */
#define	UA_NET64_INIT(_val) { {				     \
				((_val) >> 56),		    \
				((_val) >> 48) & 0xff,	    \
				((_val) >> 40) & 0xff,	    \
				((_val) >> 32) & 0xff,	    \
				((_val) >> 24) & 0xff,	    \
				((_val) >> 16) & 0xff,	    \
				((_val) >> 8) & 0xff,	    \
				((_val) >> 0) & 0xff	    \
			    } }

#endif /* _LIBSA_NET_TYPES_H_ */
