/* information about connections between hosts and clients
 * Copyright (C) 1998-2001  D. Hugh Redelmeier
 * Copyright (C) 2009-2010  Andreas Steffen - Hochschule fuer Technik Rapperswil
 *
 * This program 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 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * 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.  See the GNU General Public License
 * for more details.
 */

#ifndef _CONNECTIONS_H
#define _CONNECTIONS_H

#include <sys/queue.h>

#include <utils/host.h>
#include <utils/linked_list.h>
#include <utils/identification.h>
#include <credentials/ietf_attributes/ietf_attributes.h>

#include "certs.h"
#include "smartcard.h"
#include "whack.h"

/* There are two kinds of connections:
 * - ISAKMP connections, between hosts (for IKE communication)
 * - IPsec connections, between clients (for secure IP communication)
 *
 * An ISAKMP connection looks like:
 *   host<--->host
 *
 * An IPsec connection looks like:
 *   client-subnet<-->host<->nexthop<--->nexthop<->host<-->client-subnet
 *
 * For the connection to be relevant to this instance of Pluto,
 * exactly one of the hosts must be a public interface of our machine
 * known to this instance.
 *
 * The client subnet might simply be the host -- this is a
 * representation of "host mode".
 *
 * Each nexthop defaults to the neighbouring host's IP address.
 * The nexthop is a property of the pair of hosts, not each
 * individually.  It is only needed for IPsec because of the
 * way IPsec is mixed into the kernel routing logic.  Furthermore,
 * only this end's nexthop is actually used.  Eventually, nexthop
 * will be unnecessary.
 *
 * Other information represented:
 * - each connection has a name: a chunk of uninterpreted text
 *   that is unique for each connection.
 * - security requirements (currently just the "policy" flags from
 *   the whack command to initiate the connection, but eventually
 *   much more.  Different for ISAKMP and IPsec connections.
 * - rekeying parameters:
 *   + time an SA may live
 *   + time before SA death that a rekeying should be attempted
 *     (only by the initiator)
 *   + number of times to attempt rekeying
 * - With the current KLIPS, we must route packets for a client
 *   subnet through the ipsec interface (ipsec0).  Only one
 *   gateway can get traffic for a specific (client) subnet.
 *   Furthermore, if the routing isn't in place, packets will
 *   be sent in the clear.
 *   "routing" indicates whether the routing has been done for
 *   this connection.  Note that several connections may claim
 *   the same routing, as long as they agree about where the
 *   packets are to be sent.
 * - With the current KLIPS, only one outbound IPsec SA bundle can be
 *   used for a particular client.  This is due to a limitation
 *   of using only routing for selection.  So only one IPsec state (SA)
 *   may "own" the eroute.  "eroute_owner" is the serial number of
 *   this state, SOS_NOBODY if there is none.  "routing" indicates
 *   what kind of erouting has been done for this connection, if any.
 *
 * Details on routing is in constants.h
 *
 * Operations on Connections:
 *
 * - add a new connection (with all details) [whack command]
 * - delete a connection (by name) [whack command]
 * - initiate a connection (by name) [whack command]
 * - find a connection (by IP addresses of hosts)
 *   [response to peer request; finding ISAKMP connection for IPsec connection]
 *
 * Some connections are templates, missing the address of the peer
 * (represented by INADDR_ANY).  These are always arranged so that the
 * missing end is "that" (there can only be one missing end).  These can
 * be instantiated (turned into real connections) by Pluto in one of two
 * different ways: Road Warrior Instantiation or Opportunistic
 * Instantiation.  A template connection is marked for Opportunistic
 * Instantiation by specifying the peer client as 0.0.0.0/32 (or the IPV6
 * equivalent).  Otherwise, it is suitable for Road Warrior Instantiation.
 *
 * Instantiation creates a new temporary connection, with the missing
 * details filled in.  The resulting template lasts only as long as there
 * is a state that uses it.
 */

/* connection policy priority: how important this policy is
 * - used to implement eroute-like precedence (augmented by a small
 *   bonus for a routed connection).
 * - a whole number
 * - larger is more important
 * - three subcomponents.  In order of decreasing significance:
 *   + length of source subnet mask (8 bits)
 *   + length of destination subnet mask (8 bits)
 *   + bias (8 bit)
 * - a bias of 1 is added to allow prio BOTTOM_PRIO to be less than all
 *   normal priorities
 * - other bias values are created on the fly to give mild preference
 *   to certaion conditions (eg. routedness)
 * - priority is inherited -- an instance of a policy has the same priority
 *   as the original policy, even though its subnets might be smaller.
 * - display format: n,m
 */
typedef unsigned long policy_prio_t;
#define BOTTOM_PRIO   ((policy_prio_t)0)        /* smaller than any real prio */
#define set_policy_prio(c) { (c)->prio = \
		((policy_prio_t)(c)->spd.this.client.maskbits << 16) \
		| ((policy_prio_t)(c)->spd.that.client.maskbits << 8) \
		| (policy_prio_t)1; }
#define POLICY_PRIO_BUF (3+1+3+1)
extern void fmt_policy_prio(policy_prio_t pp, char buf[POLICY_PRIO_BUF]);

struct virtual_t;

struct end {
	identification_t *id;
	ip_address host_addr, host_nexthop;
	host_t *host_srcip;
	ip_subnet client;

	bool is_left;
	bool key_from_DNS_on_demand;
	bool has_client;
	bool has_client_wildcard;
	bool has_port_wildcard;
	bool has_id_wildcards;
	bool has_natip;
	char *updown;
	u_int16_t host_port;        /* host order */
	u_int16_t port;             /* host order */
	u_int8_t protocol;
	cert_t *cert;               /* end certificate */
	identification_t *ca;       /* CA distinguished name */
	ietf_attributes_t *groups;  /* access control groups */
	smartcard_t *sc;            /* smartcard reader and key info */
	struct virtual_t *virt;
	bool modecfg;               /* this end: request local address from server */
								/* that end: give local addresses to clients */
	char *pool;					/* name of an associated virtual IP address pool */
	bool hostaccess;            /* allow access to host via iptables INPUT/OUTPUT */
								/* rules if client behind host is a subnet */
	bool allow_any;             /* IP address is subject to change */
	certpolicy_t sendcert;      /* whether or not to send the certificate */
};

struct spd_route {
	struct spd_route *next;
	struct end this;
	struct end that;
	so_serial_t eroute_owner;
	enum routing_t routing;     /* level of routing in place */
	uint32_t reqid;
	mark_t mark_in;
	mark_t mark_out;
};

typedef struct connection connection_t;

struct connection {
	char *name;
	bool ikev1;

	lset_t policy;
	time_t sa_ike_life_seconds;
	time_t sa_ipsec_life_seconds;
	time_t sa_rekey_margin;
	unsigned long sa_rekey_fuzz;
	unsigned long sa_keying_tries;

	identification_t *xauth_identity;     /* XAUTH identity */

	/* RFC 3706 DPD */
	time_t dpd_delay;
	time_t dpd_timeout;
	dpd_action_t dpd_action;

	char              *log_file_name;     /* name of log file */
	FILE              *log_file;          /* possibly open FILE */
	TAILQ_ENTRY(connection) log_link;     /* linked list of open conns */
	bool               log_file_err;      /* only bitch once */

	struct spd_route spd;

	/* internal fields: */

	unsigned long instance_serial;
	policy_prio_t prio;
	bool instance_initiation_ok;        /* this is an instance of a policy that mandates initiate */
	enum connection_kind kind;
	const struct iface *interface;      /* filled in iff oriented */

	so_serial_t /* state object serial number */
		newest_isakmp_sa,
		newest_ipsec_sa;


#ifdef DEBUG
	lset_t extra_debugging;
#endif

	/* note: if the client is the gateway, the following must be equal */
	sa_family_t addr_family;            /* between gateways */
	sa_family_t tunnel_addr_family;     /* between clients */

	connection_t *policy_next;          /* if multiple policies,
										   next one to apply */
	struct gw_info *gw_info;
	struct alg_info_esp *alg_info_esp;
	struct alg_info_ike *alg_info_ike;
	struct host_pair *host_pair;
	connection_t *hp_next;              /* host pair list link */
	connection_t *ac_next;              /* all connections list link */
	linked_list_t *requested_ca;        /* collected certificate requests */
	linked_list_t *requested;           /* requested attributes with handlers */
	linked_list_t *attributes;          /* configuration attributes with handlers */
	bool got_certrequest;
};

#define oriented(c) ((c).interface != NULL)
extern bool orient(connection_t *c);

extern bool same_peer_ids(const connection_t *c, const connection_t *d,
						  identification_t *his_id);

/* Format the topology of a connection end, leaving out defaults.
 * Largest left end looks like: client === host : port [ host_id ] --- hop
 * Note: if that==NULL, skip nexthop
 */
#define END_BUF (SUBNETTOT_BUF + ADDRTOT_BUF + IDTOA_BUF + ADDRTOT_BUF + 10)
extern size_t format_end(char *buf, size_t buf_len, const struct end *this,
						 const struct end *that, bool is_left, lset_t policy);

extern void add_connection(const whack_message_t *wm);
extern void initiate_connection(const char *name, int whackfd);
extern void initiate_opportunistic(const ip_address *our_client,
								   const ip_address *peer_client,
								   int transport_proto, bool held, int whackfd);
extern void terminate_connection(const char *nm);
extern void release_connection(connection_t *c, bool relations);
extern void delete_connection(connection_t *c, bool relations);
extern void delete_connections_by_name(const char *name, bool strict);
extern void delete_every_connection(void);
extern char *add_group_instance(connection_t *group, const ip_subnet *target);
extern void remove_group_instance(const connection_t *group, const char *name);
extern void release_dead_interfaces(void);
extern void check_orientations(void);
extern connection_t *route_owner(connection_t *c, struct spd_route **srp,
								 connection_t **erop, struct spd_route **esrp);
extern connection_t *shunt_owner(const ip_subnet *ours, const ip_subnet *his);

extern bool uniqueIDs;  /* --uniqueids? */
extern void ISAKMP_SA_established(connection_t *c, so_serial_t serial);

#define id_is_ipaddr(id) ((id)->get_type(id) == ID_IPV4_ADDR || \
						  (id)->get_type(id) == ID_IPV6_ADDR)
extern bool his_id_was_instantiated(const connection_t *c);

struct state;   /* forward declaration of tag (defined in state.h) */

extern connection_t* con_by_name(const char *nm, bool strict);
extern connection_t* find_host_connection(const ip_address *me,
										  u_int16_t my_port,
										  const ip_address *him,
										  u_int16_t his_port, lset_t policy);
extern connection_t* refine_host_connection(const struct state *st,
											identification_t *id,
											identification_t *peer_ca);
extern connection_t* find_client_connection(connection_t *c,
											const ip_subnet *our_net,
											const ip_subnet *peer_net,
											const u_int8_t our_protocol,
											const u_int16_t out_port,
											const u_int8_t peer_protocol,
											const u_int16_t peer_port);
extern connection_t* find_connection_by_reqid(uint32_t reqid);
extern connection_t* find_connection_for_clients(struct spd_route **srp,
												 const ip_address *our_client,
												 const ip_address *peer_client,
												 int transport_proto);
extern void get_peer_ca_and_groups(connection_t *c,
								   identification_t **peer_ca,
								   ietf_attributes_t **peer_attributes);

/* instantiating routines
 * Note: connection_discard() is in state.h because all its work
 * is looking through state objects.
 */
struct gw_info; /* forward declaration of tag (defined in dnskey.h) */
struct alg_info;        /* forward declaration of tag (defined in alg_info.h) */
extern connection_t *rw_instantiate(connection_t *c,
									const ip_address *him,
									u_int16_t his_port,
									const ip_subnet *his_net,
									identification_t *his_id);

extern connection_t *oppo_instantiate(connection_t *c,
									  const ip_address *him,
									  identification_t *his_id,
									  struct gw_info *gw,
									  const ip_address *our_client,
									  const ip_address *peer_client);

extern connection_t
  *build_outgoing_opportunistic_connection(struct gw_info *gw,
										   const ip_address *our_client,
										   const ip_address *peer_client);

#define CONN_INST_BUF	BUF_LEN

extern void fmt_conn_instance(const connection_t *c, char buf[CONN_INST_BUF]);

/* operations on "pending", the structure representing Quick Mode
 * negotiations delayed until a Keying Channel has been negotiated.
 */

struct pending; /* forward declaration (opaque outside connections.c) */

extern void add_pending(int whack_sock, struct state *isakmp_sa,
						connection_t *c, lset_t policy, unsigned long try,
						so_serial_t replacing);

extern void release_pending_whacks(struct state *st, err_t story);
extern void unpend(struct state *st);
extern void update_pending(struct state *os, struct state *ns);
extern void flush_pending_by_state(struct state *st);
extern void show_pending_phase2(const struct host_pair *hp, const struct state *st);

extern void connection_discard(connection_t *c);

/* A template connection's eroute can be eclipsed by
 * either a %hold or an eroute for an instance iff
 * the template is a /32 -> /32.  This requires some special casing.
 */
#define eclipsable(sr) (subnetishost(&(sr)->this.client) && subnetishost(&(sr)->that.client))
extern long eclipse_count;
extern connection_t *eclipsed(connection_t *c, struct spd_route **);


/* print connection status */

extern void show_connections_status(bool all, const char *name);
extern int  connection_compare(const connection_t *ca
	, const connection_t *cb);
extern void update_host_pair(const char *why, connection_t *c
	, const ip_address *myaddr, u_int16_t myport
	, const ip_address *hisaddr, u_int16_t hisport);

#endif /* _CONNECTIONS_H */