/*
 *	BIRD -- The Resource Public Key Infrastructure (RPKI) to Router Protocol
 *
 *	(c) 2015 CZ.NIC
 *	(c) 2015 Pavel Tvrdik <pawel.tvrdik@gmail.com>
 *
 *	Using RTRlib: http://rpki.realmv6.org/
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#ifndef _BIRD_RPKI_H_
#define _BIRD_RPKI_H_

#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "lib/socket.h"
#include "lib/ip.h"

#include "transport.h"
#include "packets.h"

#define RPKI_TCP_PORT		323
#define RPKI_SSH_PORT		22
#define RPKI_RETRY_INTERVAL	600
#define RPKI_REFRESH_INTERVAL	3600
#define RPKI_EXPIRE_INTERVAL	7200

#define RPKI_VERSION_0		0
#define RPKI_VERSION_1		1
#define RPKI_VERSION_2		2
#define RPKI_MAX_VERSION 	RPKI_VERSION_2


/*
 * 	RPKI Cache
 */

enum rpki_cache_state {
  RPKI_CS_CONNECTING, 			/* Socket is establishing the transport connection. */
  RPKI_CS_ESTABLISHED,			/* Connection is established, socket is waiting for a Serial Notify or expiration of the refresh_interval timer */
  RPKI_CS_RESET,			/* Resetting RTR connection. */
  RPKI_CS_SYNC_START,			/* Sending a Serial/Reset Query PDU and expecting a Cache Response PDU */
  RPKI_CS_SYNC_RUNNING,			/* Receiving validation records from the RTR server. A state between Cache Response PDU and End of Data PDU */
  RPKI_CS_FAST_RECONNECT,		/* Reconnect without any waiting period */
  RPKI_CS_NO_INCR_UPDATE_AVAIL, 	/* Server is unable to answer the last Serial Query and sent Cache Reset. */
  RPKI_CS_ERROR_NO_DATA_AVAIL,		/* Server is unable to answer either a Serial Query or a Reset Query because it has no useful data available at this time. */
  RPKI_CS_ERROR_FATAL,			/* Fatal protocol error occurred. */
  RPKI_CS_ERROR_TRANSPORT,		/* Error on the transport socket occurred. */
  RPKI_CS_SHUTDOWN,			/* RTR Socket is stopped. */
};

struct rpki_cache {
  pool *pool;				/* Pool containing cache objects */
  struct rpki_proto *p;

  struct rpki_tr_sock *tr_sock;		/* Transport specific socket */
  enum rpki_cache_state state;		/* RPKI_CS_* */
  u32 session_id;
  u8 request_session_id;		/* 1: have to request new session id; 0: we have already received session id */
  u32 serial_num;			/* Serial number denotes the logical version of data from cache server */
  u8 version;				/* Protocol version */
  u8 min_version;			/* Minimum allowed protocol version */
  btime last_update;			/* Last successful synchronization with cache server */
  btime last_rx_prefix;			/* Last received prefix PDU */

  /* Intervals can be changed by cache server on the fly */
  u32 refresh_interval;			/* Actual refresh interval (in seconds) */
  u32 retry_interval;
  u32 expire_interval;
  timer *retry_timer;			/* Retry timer event */
  timer *refresh_timer;			/* Refresh timer event */
  timer *expire_timer;			/* Expire timer event */
};

const char *rpki_get_cache_ident(struct rpki_cache *cache);
const char *rpki_cache_state_to_str(enum rpki_cache_state state);


/*
 * 	Routes handling
 */

void rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);
void rpki_table_remove_roa(struct rpki_cache *cache, struct channel *channel, const net_addr_union *pfxr);

void rpki_table_add_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer, void *providers, uint providers_length);
void rpki_table_remove_aspa(struct rpki_cache *cache, struct channel *channel, u32 customer);

void rpki_start_refresh(struct rpki_proto *p);
void rpki_stop_refresh(struct rpki_proto *p);

/*
 *	RPKI Protocol Logic
 */

void rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state new_state);


/*
 * 	RPKI Timer Events
 */

const char *rpki_check_refresh_interval(uint seconds);
const char *rpki_check_retry_interval(uint seconds);
const char *rpki_check_expire_interval(uint seconds);


/*
 * 	RPKI Protocol Configuration
 */

struct rpki_proto {
  struct proto p;
  struct rpki_cache *cache;

  struct channel *roa4_channel;
  struct channel *roa6_channel;
  struct channel *aspa_channel;
  u8 refresh_channels;			/* For non-incremental updates using rt_refresh_begin(), rt_refresh_end() */
};

struct rpki_config {
  struct proto_config c;
  const char *hostname;			/* Full domain name or stringified IP address of cache server */
  ip_addr local_ip;			/* Source address to use */
  ip_addr ip;				/* IP address of cache server or IPA_NONE */
  u16 port;				/* Port number of cache server */
  struct rpki_tr_config tr_config;	/* Specific transport configuration structure */
  u32 refresh_interval;			/* Time interval (in seconds) for periodical downloading data from cache server */
  u32 retry_interval;			/* Time interval (in seconds) for an unreachable server */
  u32 expire_interval;			/* Maximal lifetime (in seconds) of ROAs without any successful refreshment */
  u8 keep_refresh_interval:1;		/* Do not overwrite refresh interval by cache server update */
  u8 keep_retry_interval:1;		/* Do not overwrite retry interval by cache server update */
  u8 keep_expire_interval:1;		/* Do not overwrite expire interval by cache server update */
  u8 ignore_max_length:1;		/* Ignore received max length and use MAX_PREFIX_LENGTH instead */
  u8 min_version;			/* Minimum version allowed */
  u8 max_version;			/* Maximum version allowed (to start with) */
};

void rpki_check_config(struct rpki_config *cf);


/*
 *	Logger
 */

#define RPKI_LOG(log_level, rpki, msg, args...) 			\
    do { 								\
      log(log_level "%s: " msg, (rpki)->p.name , ## args); 		\
    } while(0)

#if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG)
#define CACHE_DBG(cache,msg,args...) 					\
    do { 								\
      RPKI_LOG(L_DEBUG, (cache)->p, "%s [%s] %s " msg, rpki_get_cache_ident(cache), rpki_cache_state_to_str((cache)->state), __func__, ## args); \
    } while(0)
#else
#define CACHE_DBG(cache,msg,args...) do { } while(0)
#endif

#define RPKI_TRACE(level,rpki,msg,args...) 				\
    do {								\
      if ((rpki)->p.debug & level)					\
        RPKI_LOG(L_TRACE, rpki, msg, ## args);				\
    } while(0)

#define CACHE_TRACE(level,cache,msg,args...)				\
    do {								\
      if ((cache)->p->p.debug & level)					\
        RPKI_LOG(L_TRACE, (cache)->p, msg, ## args); 			\
    } while(0)

#define RPKI_WARN(p, msg, args...) RPKI_LOG(L_WARN, p, msg, ## args);

#endif /* _BIRD_RPKI_H_ */
