/* -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- */

/*
 * Copyright (c) 2001-2011 XORP, Inc and Others
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, Version 2, June
 * 1991 as published by the Free Software Foundation. Redistribution
 * and/or modification of this program under the terms of any other
 * version of the GNU General Public License is not permitted.
 * 
 * This program 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. For more details,
 * see the GNU General Public License, Version 2, a copy of which can be
 * found in the XORP LICENSE.gpl file.
 * 
 * XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
 * http://xorp.net
 */

#ifndef __PIM_PIM_PROTO_H__
#define __PIM_PIM_PROTO_H__


/*
 * Protocol Independent Multicast protocol-specific definitions
 * (both for PIM-SMv2 and PIM-DMv2, as per
 * draft-ietf-pim-sm-v2-new-11.txt and draft-ietf-pim-dm-new-v2-03.txt)
 */


/* XXX: _PIM_VT is needed if we want the extra features of <netinet/pim.h> */
#define _PIM_VT 1

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_IP6_H
#include <netinet/ip6.h>
#endif

// XXX: _PIM_VT is needed if we want the extra features of <netinet/pim.h>
#define _PIM_VT 1
#if defined(HAVE_NETINET_PIM_H) && defined(HAVE_STRUCT_PIM_PIM_VT)
#include <netinet/pim.h>
#else
#include "mrt/include/netinet/pim.h"
#endif


/*
 * Conditionally define constants that may be missing from <netinet/pim.h>
 */
#ifndef PIM_MINLEN
#define PIM_MINLEN		8	/* PIM message min. length	*/
#endif
#ifndef PIM_REG_MINLEN
#define PIM_REG_MINLEN	(PIM_MINLEN+20)	/* PIM Register hdr + inner IPv4 hdr */
#endif
#ifndef PIM6_REG_MINLEN
#define PIM6_REG_MINLEN	(PIM_MINLEN+40)	/* PIM Register hdr + inner IPv6 hdr */
#endif

/*
 * Constants definitions
 */
#ifndef IPPROTO_PIM
#define IPPROTO_PIM				103
#endif

/* PIM versions definition */
#define PIMSM_V1				1
#define PIMSM_V2				2
#define PIMSM_VERSION_MIN			PIMSM_V2
#define PIMSM_VERSION_MAX			PIMSM_V2
#define PIMSM_VERSION_DEFAULT			PIMSM_V2
#define PIMDM_V1				1
#define PIMDM_V2				2
#define PIMDM_VERSION_MIN			PIMDM_V2
#define PIMDM_VERSION_MAX			PIMDM_V2
#define PIMDM_VERSION_DEFAULT			PIMDM_V2
#define PIM_V1					(proto_is_pimsm() ?	      \
							PIMSM_V1	      \
							: PIMDM_V1)
#define PIM_V2					(proto_is_pimsm() ?	      \
							PIMSM_V2	      \
							: PIMDM_V2)
#define PIM_VERSION_MIN				(proto_is_pimsm() ?	      \
							PIMSM_VERSION_MIN     \
							: PIMDM_VERSION_MIN)
#define PIM_VERSION_MAX				(proto_is_pimsm() ?	      \
							PIMSM_VERSION_MAX     \
							: PIMDM_VERSION_MAX)
#define PIM_VERSION_DEFAULT			(proto_is_pimsm() ?	      \
							PIMSM_VERSION_DEFAULT \
							: PIMDM_VERSION_DEFAULT)

/*
 * Protocol messages specific definitions.
 * XXX: all intervals are in seconds.
 */

/* PIM_HELLO-related definitions */
#define PIM_HELLO_HOLDTIME_OPTION		1
#define PIM_HELLO_HOLDTIME_LENGTH		2
#define PIM_HELLO_HOLDTIME_FOREVER		0xffff
#define PIM_HELLO_LAN_PRUNE_DELAY_OPTION	2
#define PIM_HELLO_LAN_PRUNE_DELAY_LENGTH	4
#define PIM_HELLO_LAN_PRUNE_DELAY_TBIT		((uint16_t)(1 << 15))
#define PIM_HELLO_DR_PRIORITY_OPTION		19
#define PIM_HELLO_DR_PRIORITY_LENGTH		4
#define PIM_HELLO_DR_PRIORITY_DEFAULT		1
#define PIM_HELLO_DR_PRIORITY_LOWEST		0
#define PIM_HELLO_DR_PRIORITY_HIGHEST		0xffffffffU
#define PIM_HELLO_GENID_OPTION			20
#define PIM_HELLO_GENID_LENGTH			4
#define PIM_HELLO_ADDRESS_LIST_OPTION		24

#define PIM_HELLO_HELLO_TRIGGERED_DELAY_DEFAULT	5
#define PIM_HELLO_HELLO_PERIOD_DEFAULT		30
#define PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO	3.5
#define PIM_HELLO_HELLO_HOLDTIME_DEFAULT	((int)(PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO * PIM_HELLO_HELLO_PERIOD_DEFAULT))

#define PIM_PROPAGATION_DELAY_MSEC_DEFAULT	500	/* Propagation_delay_default */
#define PIM_OVERRIDE_INTERVAL_MSEC_DEFAULT	2500   /* t_override_default */


/* PIM_JOIN_PRUNE-related definitions */
#define PIM_JOIN_PRUNE_PERIOD_DEFAULT		60
#define PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO	3.5
#define PIM_JOIN_PRUNE_HOLDTIME_DEFAULT		((int)(PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO * PIM_JOIN_PRUNE_PERIOD_DEFAULT))
#define PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MIN 1.1
#define PIM_JOIN_PRUNE_SUPPRESSION_TIMEOUT_RANDOM_FACTOR_MAX 1.4
#define PIM_JOIN_PRUNE_OIF_HOLDTIME_FOREVER	0xffff

/* PIM_ASSERT-related definitions */
#define PIM_ASSERT_ASSERT_TIME_DEFAULT		180
#define PIM_ASSERT_ASSERT_OVERRIDE_INTERVAL_DEFAULT 3
#define PIM_ASSERT_MAX_METRIC_PREFERENCE	0x7fffffffU
#define PIM_ASSERT_MAX_METRIC			0xffffffffU
#define PIM_ASSERT_OIF_RATE_LIMIT		1	/* 1 pkt/second */
#define PIM_ASSERT_RPT_BIT			((uint32_t)(1 << 31))

/* PIM_REGISTER-related definitions */
#define PIM_REGISTER_SUPPRESSION_TIME_DEFAULT	60
#define PIM_REGISTER_PROBE_TIME_DEFAULT		5
#define PIM_REGISTER_HEADER_LENGTH		8

/* PIM_CAND_RP_ADV-related definitions */
#define PIM_CAND_RP_ADV_PERIOD_DEFAULT		60
#define PIM_CAND_RP_ADV_RP_HOLDTIME_DEFAULT	((int)(2.5 * PIM_CAND_RP_ADV_PERIOD_DEFAULT))
#define PIM_CAND_RP_ADV_RP_PRIORITY_DEFAULT	192

/* PIM_BOOTSTRAP-related definitions */
#define PIM_BOOTSTRAP_LOWEST_PRIORITY		0	/* Larger is better */
#define PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT	60
#define PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT	((int)(2 * PIM_BOOTSTRAP_BOOTSTRAP_PERIOD_DEFAULT + 10))
#define PIM_BOOTSTRAP_RAND_OVERRIDE_DEFAULT	5
#define PIM_BOOTSTRAP_SCOPE_ZONE_TIMEOUT_DEFAULT ((int)(10 * PIM_BOOTSTRAP_BOOTSTRAP_TIMEOUT_DEFAULT))
#define PIM_BOOTSTRAP_HASH_MASK_LEN_IPV4_DEFAULT	30
#define PIM_BOOTSTRAP_HASH_MASK_LEN_IPV6_DEFAULT	126
#define PIM_BOOTSTRAP_HASH_MASK_LEN_DEFAULT(ip_family)			\
			((ip_family == AF_INET)?			\
				PIM_BOOTSTRAP_HASH_MASK_LEN_IPV4_DEFAULT \
				: PIM_BOOTSTRAP_HASH_MASK_LEN_IPV6_DEFAULT)

/* PIM_GRAFT-related definitions */
#define PIM_GRAFT_RETRY_PERIOD_DEFAULT		3

/* PIM-DM-related definitions */
#define PIM_DM_SOURCE_LIFETIME_DEFAULT		210
#define PIM_DM_REFRESH_INTERVAL_DEFAULT		60

/* Other timeout default values */
#define PIM_KEEPALIVE_PERIOD_DEFAULT		210
#define PIM_RP_KEEPALIVE_PERIOD_DEFAULT		(3 * PIM_REGISTER_SUPPRESSION_TIME_DEFAULT + PIM_REGISTER_PROBE_TIME_DEFAULT)

/*
 * Structures, typedefs and macros
 */
/*
 * Address-family related definitions. See this link for a
 * complete listing of all address families:
 *    http://www.isi.edu/in-notes/iana/assignments/address-family-numbers
 */
#define ADDRF_IPv4		1
#define ADDRF_IPv6		2
#define ADDRF_NATIVE_ENCODING	0		/* Type of encoding within
						 * a specific address family
						 */

#ifndef HAVE_IPV6
#define ADDRF2IP_ADDRF(addr_family)					\
		(((addr_family) == ADDRF_IPv4) ? (AF_INET) : (-1))
#define IP_ADDRF2ADDRF(ip_family)					\
		(((ip_family) == AF_INET) ? (ADDRF_IPv4) : (-1))

#else
#define ADDRF2IP_ADDRF(addr_family)					\
		(((addr_family) == ADDRF_IPv4) ? (AF_INET) 		\
			: ((addr_family) == ADDRF_IPv6) ?		\
				(AF_INET6)				\
				: (-1))
#define IP_ADDRF2ADDRF(ip_family)					\
		(((ip_family) == AF_INET) ? (ADDRF_IPv4)		\
			: ((ip_family) == AF_INET6) ?			\
				(ADDRF_IPv6)				\
				: (-1))

#endif /* HAVE_IPV6 */

#define ESADDR_RPT_BIT		0x1
#define ESADDR_WC_BIT		0x2
#define ESADDR_S_BIT		0x4

#define EGADDR_Z_BIT		0x1

/* PIM message max. payload (IP header and Router Alert IP option excluded) */
#ifndef HAVE_IPV6
#ifndef HOST_OS_WINDOWS
#define PIM_MAXPACKET(ip_family) (((ip_family) == AF_INET) ?		\
					(IP_MAXPACKET			\
						- sizeof(struct ip)	\
						- 4*sizeof(uint8_t))	\
					: (0))
#else /* HOST_OS_WINDOWS */

#define PIM_MAXPACKET(ip_family)	65496

#endif /* !HOST_OS_WINDOWS */
#else
#ifndef IPV6_MAXPACKET
#define IPV6_MAXPACKET 65535	/* ip6 max packet size without Jumbo payload */
#endif

#define PIM_MAXPACKET(ip_family) (((ip_family) == AF_INET) ?		\
					(IP_MAXPACKET			\
					 - sizeof(struct ip)		\
					- 4*sizeof(uint8_t))		\
					: ((ip_family) == AF_INET6) ?	\
						(IPV6_MAXPACKET		\
						- 4*sizeof(uint8_t))	\
						: (0))
#endif /* HAVE_IPV6 */

/*
 * Macros to get PIM-specific encoded addresses from a datastream.
 *
 * XXX: the macros below assume that the code has the following
 * labels that can be used to jump and process the particular error:
 *	'rcvd_family_error' 'rcvd_mask_len_error' 'rcvlen_error'
 * The also assume that function family() is defined (to return
 * the IP address family within the current context.
 */
/* XXX: family2addr_bytelen should be defined somewhere */
#ifndef FAMILY2ADDRSIZE
#define FAMILY2ADDRSIZE(ip_family) family2addr_bytelen(ip_family)
#endif
#ifndef FAMILY2PREFIXLEN
#define FAMILY2PREFIXLEN(ip_family) family2addr_bitlen(ip_family)
#endif
#define GET_ENCODED_UNICAST_ADDR(rcvd_family, unicast_ipaddr, buffer)	\
do {									\
	int addr_family_;						\
									\
	BUFFER_GET_OCTET(addr_family_, (buffer));			\
	(rcvd_family) = ADDRF2IP_ADDRF(addr_family_);			\
	if ((rcvd_family) != family())					\
		goto rcvd_family_error;					\
	BUFFER_GET_SKIP(1, (buffer));		/* Encoding type */	\
	BUFFER_GET_IPADDR((rcvd_family), (unicast_ipaddr), (buffer));	\
} while (0)
#define ENCODED_UNICAST_ADDR_SIZE(ip_family)				\
			(2*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family))

#define GET_ENCODED_GROUP_ADDR(rcvd_family, group_ipaddr, mask_len, reserved, buffer) \
do {									\
	int addr_family_;						\
									\
	BUFFER_GET_OCTET(addr_family_, (buffer));			\
	(rcvd_family) = ADDRF2IP_ADDRF(addr_family_);			\
	if ((rcvd_family) != family())					\
		goto rcvd_family_error;					\
	BUFFER_GET_SKIP(1, (buffer));		/* Encoding type */	\
	BUFFER_GET_OCTET((reserved), (buffer));				\
	BUFFER_GET_OCTET((mask_len), (buffer));				\
	BUFFER_GET_IPADDR((rcvd_family), (group_ipaddr), (buffer));	\
	if ((u_int)(mask_len) > FAMILY2PREFIXLEN((rcvd_family)))	\
		goto rcvd_mask_len_error;				\
} while (0)
#define ENCODED_GROUP_ADDR_SIZE(ip_family)				\
			(4*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family))

#define GET_ENCODED_SOURCE_ADDR(rcvd_family, source_ipaddr, mask_len, flags, buffer) \
do {									\
	int addr_family_;						\
									\
	BUFFER_GET_OCTET(addr_family_, (buffer));			\
	(rcvd_family) = ADDRF2IP_ADDRF(addr_family_);			\
	if ((rcvd_family) != family())					\
		goto rcvd_family_error;					\
	BUFFER_GET_SKIP(1, (buffer));		/* Encoding type */	\
	BUFFER_GET_OCTET((flags), (buffer));				\
	BUFFER_GET_OCTET((mask_len), (buffer));				\
	BUFFER_GET_IPADDR((rcvd_family), (source_ipaddr), (buffer));	\
	if ((u_int)(mask_len) > FAMILY2PREFIXLEN((rcvd_family)))	\
		goto rcvd_mask_len_error;				\
} while (0)
#define ENCODED_SOURCE_ADDR_SIZE(ip_family)				\
			(4*sizeof(uint8_t) + FAMILY2ADDRSIZE(ip_family))

/*
 * Macros to put PIM-specific encoded addresses to a datastream.
 *
 * XXX: the macros below assume that the code has the following
 * labels that can be used to jump and process the particular error:
 *	'invalid_addr_family_error' 'buflen_error'
 */
#define PUT_ENCODED_UNICAST_ADDR(ip_family, unicast_ipaddr, buffer)	\
do {									\
	int addr_family_;						\
									\
	addr_family_ = IP_ADDRF2ADDRF((ip_family));			\
	if (addr_family_ < 0)						\
		goto invalid_addr_family_error;				\
	BUFFER_PUT_OCTET(addr_family_, (buffer));			\
	BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer));		\
	BUFFER_PUT_IPADDR((unicast_ipaddr), (buffer));			\
} while (0)

#define PUT_ENCODED_GROUP_ADDR(ip_family, group_ipaddr, mask_len, reserved, buffer)\
do {									\
	int addr_family_;						\
									\
	addr_family_ = IP_ADDRF2ADDRF((ip_family));			\
	if (addr_family_ < 0)						\
		goto invalid_addr_family_error;				\
	BUFFER_PUT_OCTET(addr_family_, (buffer));			\
	BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer));		\
	BUFFER_PUT_OCTET(reserved, (buffer));	/* E.g., EGADDR_Z_BIT */\
	BUFFER_PUT_OCTET((mask_len), (buffer));				\
	BUFFER_PUT_IPADDR((group_ipaddr), (buffer));			\
} while (0)

#define PUT_ENCODED_SOURCE_ADDR(ip_family, source_ipaddr, mask_len, flags, buffer) \
do {									\
	int addr_family_;						\
									\
	addr_family_ = IP_ADDRF2ADDRF((ip_family));			\
	if (addr_family_ < 0)						\
		goto invalid_addr_family_error;				\
	BUFFER_PUT_OCTET(addr_family_, (buffer));			\
	BUFFER_PUT_OCTET(ADDRF_NATIVE_ENCODING, (buffer));		\
	BUFFER_PUT_OCTET((flags), (buffer));				\
	BUFFER_PUT_OCTET((mask_len), (buffer));				\
	BUFFER_PUT_IPADDR((source_ipaddr), (buffer));			\
} while (0)

#define BUFFER_PUT_SKIP_PIM_HEADER(buffer)				\
do {									\
	BUFFER_PUT_SKIP(sizeof(struct pim), (buffer));			\
} while (0)

/*
 * The ASCII names of the PIM protocol control messages
 */
#define PIMTYPE2ASCII(t)						\
(((t) == PIM_HELLO) ?							\
    "PIM_HELLO"								\
    : ((t) == PIM_REGISTER) ?						\
	"PIM_REGISTER"							\
	: ((t) == PIM_REGISTER_STOP) ?					\
	    "PIM_REGISTER_STOP"						\
	    : ((t) == PIM_JOIN_PRUNE) ?					\
		"PIM_JOIN_PRUNE"					\
		: ((t) == PIM_BOOTSTRAP) ?				\
		    "PIM_BOOTSTRAP"					\
		    : ((t) == PIM_ASSERT) ?				\
			"PIM_ASSERT"					\
			: ((t) == PIM_GRAFT) ?				\
			    "PIM_GRAFT"					\
			    : ((t) == PIM_GRAFT_ACK) ?			\
				"PIM_GRAFT_ACK"				\
				: ((t) == PIM_CAND_RP_ADV) ?		\
				    "PIM_CAND_RP_ADV"			\
				    : ((t) == PIM_ALL_DF_ELECTION) ?	\
					"PIM_ALL_DF_ELECTION"		\
					: "PIM_type_unknown")

/*
 * Global variables
 */

/*
 * Global functions prototypes
 */
__BEGIN_DECLS

__END_DECLS

#endif /* __PIM_PIM_PROTO_H__ */
