freeDiameter.h 33.3 KB
Newer Older
Sebastien Decugis's avatar
Sebastien Decugis committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
*													 *
* Copyright (c) 2009, WIDE Project and NICT								 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

#ifndef _FREEDIAMETER_H
#define _FREEDIAMETER_H


40
#include <freeDiameter/libfreeDiameter.h>
Sebastien Decugis's avatar
Sebastien Decugis committed
41
42
43
44
45
46
47
48
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

/* GNUTLS version */
#ifndef GNUTLS_VERSION
#define GNUTLS_VERSION LIBGNUTLS_VERSION
#endif /* GNUTLS_VERSION */

49
50
51
52
53
/* GNUTLS calls debug level */
#ifndef GNUTLS_DBG_LEVEL
#define GNUTLS_DBG_LEVEL ANNOYING
#endif /* GNUTLS_DBG_LEVEL */

Sebastien Decugis's avatar
Sebastien Decugis committed
54
55
56
/* Check the return value of a GNUTLS function, log and propagate */
#define CHECK_GNUTLS_DO( __call__, __fallback__ ) {						\
	int __ret__;										\
57
	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );				\
Sebastien Decugis's avatar
Sebastien Decugis committed
58
59
60
61
62
63
	__ret__ = (__call__);									\
	if (__ret__ < 0) {									\
		TRACE_DEBUG(INFO, "Error in '" #__call__ "':\t%s", gnutls_strerror(__ret__));	\
		__fallback__;									\
	}											\
}
Sebastien Decugis's avatar
Sebastien Decugis committed
64

65
66
67
68
69
70
71
/* For GNUTLS routines that do not return a value */
#define GNUTLS_TRACE( __call__) {					\
	TRACE_DEBUG(GNUTLS_DBG_LEVEL, "GNUTLS call: " #__call__ );	\
	(__call__);							\
}


Sebastien Decugis's avatar
Sebastien Decugis committed
72

Sebastien Decugis's avatar
Sebastien Decugis committed
73
74
/* Structure to hold the configuration of the freeDiameter daemon */
struct fd_config {
75
76
	int		 cnf_eyec;	/* Eye catcher: EYEC_CONFIG */
			#define	EYEC_CONFIG	0xC011F16
Sebastien Decugis's avatar
Sebastien Decugis committed
77
	
78
	char		*cnf_file;	/* Configuration file to parse, default is DEFAULT_CONF_FILE */
Sebastien Decugis's avatar
Sebastien Decugis committed
79
	
80
81
82
83
84
85
86
87
88
89
90
91
92
	char   		*cnf_diamid;	/* Diameter Identity of the local peer (FQDN -- UTF-8) */
	size_t		 cnf_diamid_len;	/* length of the previous string */
	char		*cnf_diamrlm;	/* Diameter realm of the local peer, default to realm part of diam_id */
	size_t		 cnf_diamrlm_len;/* length of the previous string */
	
	unsigned int	 cnf_timer_tc;	/* The value in seconds of the default Tc timer */
	unsigned int 	 cnf_timer_tw;	/* The value in seconds of the default Tw timer */
	
	uint16_t	 cnf_port;	/* the local port for legacy Diameter (default: 3868) in host byte order */
	uint16_t	 cnf_port_tls;	/* the local port for Diameter/TLS (default: 3869) in host byte order */
	uint16_t	 cnf_sctp_str;	/* default max number of streams for SCTP associations (def: 30) */
	struct fd_list	 cnf_endpoints;	/* the local endpoints to bind the server to. list of struct fd_endpoint. default is empty (bind all) */
	struct fd_list	 cnf_apps;	/* Applications locally supported (except relay, see flags). Use fd_disp_app_support to add one. list of struct fd_app. */
Sebastien Decugis's avatar
Sebastien Decugis committed
93
	struct {
94
		unsigned no_fwd : 1;	/* the peer does not relay messages (0xffffff app id) */
Sebastien Decugis's avatar
Sebastien Decugis committed
95
96
97
98
99
100
		unsigned no_ip4 : 1;	/* disable IP */
		unsigned no_ip6 : 1;	/* disable IPv6 */
		unsigned no_tcp : 1;	/* disable use of TCP */
		unsigned no_sctp: 1;	/* disable the use of SCTP */
		unsigned pr_tcp	: 1;	/* prefer TCP over SCTP */
		unsigned tls_alg: 1;	/* TLS algorithm for initiated cnx. 0: separate port. 1: inband-security (old) */
101
	} 		 cnf_flags;
Sebastien Decugis's avatar
Sebastien Decugis committed
102
	
Sebastien Decugis's avatar
Sebastien Decugis committed
103
	struct {
Sebastien Decugis's avatar
Sebastien Decugis committed
104
105
106
107
108
		/* Credentials parameters (backup) */
		char *  			 cert_file;
		char *				 key_file;
		
		char *  			 ca_file;
109
		int				 ca_file_nr;
Sebastien Decugis's avatar
Sebastien Decugis committed
110
111
112
113
114
115
116
117
118
119
120
121
		char *  			 crl_file;
		
		char *				 prio_string;
		unsigned int 			 dh_bits;
		
		/* GNUTLS parameters */
		gnutls_priority_t 		 prio_cache;
		gnutls_dh_params_t 		 dh_cache;
		
		/* GNUTLS server credential(s) */
		gnutls_certificate_credentials_t credentials;
		
Sebastien Decugis's avatar
Sebastien Decugis committed
122
123
	} 		 cnf_sec_data;
	
124
125
126
	uint32_t	 cnf_orstateid;	/* The value to use in Origin-State-Id, default to random value */
	struct dictionary *cnf_dict;	/* pointer to the global dictionary */
	struct fifo	  *cnf_main_ev;	/* events for the daemon's main (struct fd_event items) */
Sebastien Decugis's avatar
Sebastien Decugis committed
127
};
128
extern struct fd_config *fd_g_config; /* The pointer to access the global configuration, initalized in main */
Sebastien Decugis's avatar
Sebastien Decugis committed
129

130
131
132
133
134
135
136
137

/***************************************/
/*   Peers information                 */
/***************************************/

/* States of a peer */
enum peer_state {
	/* Stable states */
Sebastien Decugis's avatar
Sebastien Decugis committed
138
	STATE_NEW = 0,		/* The peer has been just been created, PSM thread not started yet */
139
140
141
142
143
144
145
146
147
148
149
150
	STATE_OPEN,		/* Connexion established */
	
	/* Peer state machine */
	STATE_CLOSED,		/* No connection established, will re-attempt after TcTimer. */
	STATE_CLOSING,		/* the connection is being shutdown (DPR/DPA in progress) */
	STATE_WAITCNXACK,	/* Attempting to establish transport-level connection */
	STATE_WAITCNXACK_ELEC,	/* Received a CER from this same peer on an incoming connection (other peer object), while we were waiting for cnx ack */
	STATE_WAITCEA,		/* Connection established, CER sent, waiting for CEA */
	/* STATE_WAITRETURNS_ELEC, */	/* This state is not stable and therefore deprecated:
				   We have sent a CER on our initiated connection, and received a CER from the remote peer on another connection. Election.
				   If we win the election, we must disconnect the initiated connection and send a CEA on the other => we go to OPEN state.
				   If we lose, we disconnect the other connection (receiver) and fallback to WAITCEA state. */
Sebastien Decugis's avatar
Sebastien Decugis committed
151
	STATE_OPEN_HANDSHAKE,	/* TLS Handshake and validation are in progress in open state -- we use it only for debug purpose, it is never displayed */
152
153
154
	
	/* Failover state machine */
	STATE_SUSPECT,		/* A DWR was sent and not answered within TwTime. Failover in progress. */
155
	STATE_REOPEN,		/* Connection has been re-established, waiting for 3 DWR/DWA exchanges before putting back to service */
Sebastien Decugis's avatar
Sebastien Decugis committed
156
157
158
159
	
	/* Error state */
	STATE_ZOMBIE		/* The PSM thread is not running anymore; it must be re-started or peer should be deleted. */
#define STATE_MAX STATE_ZOMBIE
160
};
Sebastien Decugis's avatar
Sebastien Decugis committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* The following macro is called in freeDiameter/p_psm.c */
#define DECLARE_STATE_STR()		\
const char *peer_state_str[] = { 	\
	  "STATE_NEW"			\
	, "STATE_OPEN"			\
	, "STATE_CLOSED"		\
	, "STATE_CLOSING"		\
	, "STATE_WAITCNXACK"		\
	, "STATE_WAITCNXACK_ELEC"	\
	, "STATE_WAITCEA"		\
	, "STATE_OPEN_HANDSHAKE"	\
	, "STATE_SUSPECT"		\
	, "STATE_REOPEN"		\
	, "STATE_ZOMBIE"		\
	};
extern const char *peer_state_str[];
177
#define STATE_STR(state) \
Sebastien Decugis's avatar
Sebastien Decugis committed
178
	(((unsigned)(state)) <= STATE_MAX ? peer_state_str[((unsigned)(state)) ] : "<Invalid>")
179

180
/* Information about a remote peer */
181
182
struct peer_info {
	
183
	char * 		pi_diamid;	/* UTF-8, \0 terminated. The Diameter Identity of the remote peer. */
184
185
	
	struct {
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
		struct {
			#define PI_P3_DEFAULT	0	/* Use any available protocol */
			#define PI_P3_IP	1	/* Use only IP to connect to this peer */
			#define PI_P3_IPv6	2	/* resp, IPv6 */
			unsigned	pro3 :2;

			#define PI_P4_DEFAULT	0	/* Attempt any available protocol */
			#define PI_P4_TCP	1	/* Only use TCP */
			#define PI_P4_SCTP	2	/* Only use SCTP */
			unsigned	pro4 :2;

			#define PI_ALGPREF_SCTP	0	/* SCTP is  attempted first (default) */
			#define PI_ALGPREF_TCP	1	/* TCP is attempted first */
			unsigned	alg :1;

			#define PI_SEC_DEFAULT	0	/* New TLS security (handshake after connection, protecting also CER/CEA) */
			#define PI_SEC_NONE	1	/* Transparent security with this peer (IPsec) */
			#define PI_SEC_TLS_OLD	2	/* Old TLS security (use Inband-Security-Id AVP during CER/CEA) */
			unsigned	sec :2;		/* Set sec = 3 to authorize use of (Inband-Security-Id == NONE) with this peer, sec = 2 only authorizing TLS */

			#define PI_EXP_NONE	0	/* the peer entry does not expire */
			#define PI_EXP_INACTIVE	1	/* the peer entry expires (i.e. is deleted) after pi_lft seconds without activity */
			unsigned	exp :1;

			#define PI_PRST_NONE	0	/* the peer entry is deleted after disconnection / error */
			#define PI_PRST_ALWAYS	1	/* the peer entry is persistant (will be kept as ZOMBIE in case of error) */
			unsigned	persist :1;
			
		}		pic_flags;	/* Flags influencing the connection to the remote peer */
215
		
216
217
		char * 		pic_realm;	/* If configured, the daemon will match the received realm in CER/CEA matches this. */
		uint16_t	pic_port; 	/* port to connect to. 0: default. */
218
		
219
220
221
		uint32_t 	pic_lft;	/* lifetime of this peer when inactive (see pic_flags.exp definition) */
		int		pic_tctimer; 	/* use this value for TcTimer instead of global, if != 0 */
		int		pic_twtimer; 	/* use this value for TwTimer instead of global, if != 0 */
222
		
223
		char *		pic_priority;	/* Priority string for GnuTLS if we don't use the default */
224
		
225
226
227
	} config;	/* Configured data (static for this peer entry) */
	
	struct {
228
		
229
		enum peer_state	pir_state;	/* Current state of the peer in the state machine */
Sebastien Decugis's avatar
Backup    
Sebastien Decugis committed
230
		
231
		char * 		pir_realm;	/* The received realm in CER/CEA. */
232
		
233
234
235
236
237
238
		uint32_t	pir_vendorid;	/* Content of the Vendor-Id AVP, or 0 by default */
		uint32_t	pir_orstate;	/* Origin-State-Id value */
		char *		pir_prodname;	/* copy of UTF-8 Product-Name AVP (\0 terminated) */
		uint32_t	pir_firmrev;	/* Content of the Firmware-Revision AVP */
		int		pir_relay;	/* The remote peer advertized the relay application */
		struct fd_list	pir_apps;	/* applications advertised by the remote peer, except relay (pi_flags.relay) */
239
		int		pir_isi;	/* Inband-Security-Id advertised (PI_SEC_* bits) */
240
		
Sebastien Decugis's avatar
Sebastien Decugis committed
241
242
		uint32_t	pir_lastDC;	/* The last Disconnect-Cause value received */
		
243
244
245
246
247
248
249
		int		pir_proto;	/* The L4 protocol currently used with the peer (IPPROTO_TCP or IPPROTO_SCTP) */
		const gnutls_datum_t 	*pir_cert_list; 	/* The (valid) credentials that the peer has presented, or NULL if TLS is not used */
								/* This is inspired from http://www.gnu.org/software/gnutls/manual/gnutls.html#ex_003ax509_002dinfo 
								   see there for example of using this data */
		unsigned int 	pir_cert_list_size;		/* Number of certificates in the list */
		
	} runtime;	/* Data populated after connection, may change between 2 connections -- not used by fd_peer_add */
250
	
251
	struct fd_list	pi_endpoints;	/* Endpoint(s) of the remote peer (configured, discovered, or advertized). list of struct fd_endpoint. DNS resolved if empty. */
252
253
};

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
struct peer_hdr {
	struct fd_list	 chain;	/* List of all the peers, ordered by their Diameter Id */
	struct peer_info info;	/* The public data */
	
	/* This header is followed by more data in the private peer structure definition */
};

/* the global list of peers. 
  Since we are not expecting so many connections, we don't use a hash, but it might be changed.
  The list items are peer_hdr structures (actually, fd_peer, but the cast is OK) */
extern struct fd_list fd_g_peers;
extern pthread_rwlock_t fd_g_peers_rw; /* protect the list */

/*
 * FUNCTION:	fd_peer_add
 *
 * PARAMETERS:
 *  info 	: Information to create the peer.
272
 *  orig_dbg	: A string indicating the origin of the peer information, for debug (ex: conf, redirect, ...)
273
274
275
276
277
 *  cb		: optional, a callback to call (once) when the peer connection is established or failed
 *  cb_data	: opaque data to pass to the callback.
 *
 * DESCRIPTION: 
 *  Add a peer to the list of peers to which the daemon must maintain a connexion.
278
279
280
281
282
283
 *
 *  The content of info parameter is copied, except for the list of endpoints if 
 * not empty, which is simply moved into the created object. It means that the list
 * items must have been malloc'd, so that they can be freed.
 *
 *  If cb is not null, the callback is called when the connection is in OPEN state or
284
 * when an error has occurred. The callback should use the pi_state information to 
285
286
287
288
289
 * determine which one it is. If the first parameter of the called callback is NULL, it 
 * means that the peer is being destroyed before attempt success / failure. 
 * cb is called to allow freeing cb_data in  * this case.
 *
 *  The orig_dbg string is only useful for easing debug, and can be left to NULL.
290
291
292
293
294
295
296
297
 *
 * RETURN VALUE:
 *  0      	: The peer is added.
 *  EINVAL 	: A parameter is invalid.
 *  EEXIST 	: A peer with the same Diameter-Id is already in the list.
 *  (other standard errors may be returned, too, with their standard meaning. Example:
 *    ENOMEM 	: Memory allocation for the new object element failed.)
 */
298
int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data );
299

Sebastien Decugis's avatar
Sebastien Decugis committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*
 * FUNCTION:	fd_peer_getbyid
 *
 * PARAMETERS:
 *  diamid 	: A \0 terminated string.
 *  peer	: The peer is stored here if it exists.
 *
 * DESCRIPTION: 
 *   Search a peer by its Diameter-Id.
 *
 * RETURN VALUE:
 *  0   : *peer has been updated (to NULL if the peer is not found).
 * !0	: An error occurred.
 */
int fd_peer_getbyid( char * diamid, struct peer_hdr ** peer );

316
/*
317
 * FUNCTION:	fd_peer_validate_register
318
319
320
321
322
323
324
325
 *
 * PARAMETERS:
 *  peer_validate 	: Callback as defined bellow.
 *
 * DESCRIPTION: 
 *  Add a callback to authorize / reject incoming peer connections.
 * All registered callbacks are called until a callback sets auth = -1 or auth = 1.
 * If no callback returns a clear decision, the default behavior is applied (reject unknown connections)
Sebastien Decugis's avatar
Sebastien Decugis committed
326
 * The callbacks are called in FILO order of their registration.
327
328
329
330
331
 *
 * RETURN VALUE:
 *  0   : The callback is added.
 * !0	: An error occurred.
 */
332
int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) );
333
334
335
336
337
338
/*
 * CALLBACK:	peer_validate
 *
 * PARAMETERS:
 *   info     : Structure containing information about the peer attempting the connection.
 *   auth     : Store there the result if the peer is accepted (1), rejected (-1), or unknown (0).
339
 *   cb2      : If != NULL and in case of PI_SEC_TLS_OLD, another callback to call after handshake (if auth = 1).
340
341
342
 *
 * DESCRIPTION: 
 *   This callback is called when a new connection is being established from an unknown peer,
343
344
 * after the CER is received. An extension must register such callback with peer_validate_register.
 *
345
346
347
 *   The callback can learn if the peer has sent Inband-Security-Id AVPs in runtime.pir_isi fields.
 * It can also learn if a handshake has already been performed in runtime.pir_cert_list field.
 * The callback must set the value of config.pic_flags.sec appropriately to allow a connection without TLS.
348
 *
349
 *   If the old TLS mechanism is used,
350
351
352
 * the extension may also need to check the credentials provided during the TLS
 * exchange (remote certificate). For this purpose, it may set the address of a new callback
 * to be called once the handshake is completed. This new callback receives the information
353
 * structure as parameter (with pir_cert_list set) and returns 0 if the credentials are correct,
354
355
 * or an error code otherwise. If the error code is received, the connection is closed and the 
 * peer is destroyed.
356
357
 * Note that freeDiameter already achieves some usual checks. The callback may be used to enforce
 * additional restrictions.
358
359
360
361
362
 *
 * RETURN VALUE:
 *  0      	: The authorization decision has been written in the location pointed by auth.
 *  !0 		: An error occurred.
 */
363

Sebastien Decugis's avatar
Sebastien Decugis committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/***************************************/
/*   Sending a message on the network  */
/***************************************/

/*
 * FUNCTION:	fd_msg_send
 *
 * PARAMETERS:
 *  pmsg 	: Location of the message to be sent on the network (set to NULL on function return to avoid double deletion).
 *  anscb	: A callback to be called when answer is received, if msg is a request (optional)
 *  anscb_data	: opaque data to be passed back to the anscb when it is called.
 *
 * DESCRIPTION: 
 *   Sends a message on the network. (actually simply queues it in a global queue, to be picked by a daemon's thread)
 * For requests, the end-to-end id must be set (see fd_msg_get_eteid / MSGFL_ALLOC_ETEID).
 * For answers, the message must be created with function fd_msg_new_answ.
 *
 * The routing module will handle sending to the correct peer, usually based on the Destination-Realm / Destination-Host AVP.
 *
 * If the msg is a request, there are two ways of receiving the answer:
384
 *  - either having registered a callback in the dispatch module (see fd_disp_register)
Sebastien Decugis's avatar
Sebastien Decugis committed
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
 *  - or provide a callback as parameter here. If such callback is provided, it is called before the dispatch callbacks.
 *    The prototype for this callback function is:
 *     void anscb(void * data, struct msg ** answer)
 *	where:
 *		data   : opaque data that was registered along with the callback.
 *		answer : location of the pointer to the answer.
 *      note1: on function return, if *answer is not NULL, the message is passed to the dispatch module for regular callbacks.
 *	       otherwise, the callback must take care of freeing the message (msg_free).
 *	note2: the opaque data is not freed by the daemon in any case, extensions should ensure clean handling in waaad_ext_fini.
 * 
 * If no callback is registered to handle an answer, the message is discarded and an error is logged.
 *
 * RETURN VALUE:
 *  0      	: The message has been queued for sending (sending may fail asynchronously).
 *  EINVAL 	: A parameter is invalid (ex: anscb provided but message is not a request).
 *  ...
 */
int fd_msg_send ( struct msg ** pmsg, void (*anscb)(void *, struct msg **), void * data );

/*
 * FUNCTION:	fd_msg_rescode_set
 *
 * PARAMETERS:
 *  msg		: A msg object -- it must be an answer.
 *  rescode	: The name of the returned error code (ex: "DIAMETER_INVALID_AVP")
 *  errormsg    : (optional) human-readable error message to put in Error-Message AVP
 *  optavp	: (optional) If provided, the content will be put inside a Failed-AVP
 *  type_id	: 0 => nothing; 1 => adds Origin-Host and Origin-Realm with local info. 2=> adds Error-Reporting-Host.
 *
 * DESCRIPTION: 
 *   This function adds a Result-Code AVP to a message, and optionally
 *  - sets the 'E' error flag in the header,
 *  - adds Error-Message, Error-Reporting-Host and Failed-AVP AVPs.
 *
 * RETURN VALUE:
 *  0      	: Operation complete.
 *  !0      	: an error occurred.
 */
423
int fd_msg_rescode_set( struct msg * msg, char * rescode, char * errormsg, struct avp * optavp, int type_id );
Sebastien Decugis's avatar
Sebastien Decugis committed
424

425
426
/* Add Origin-Host, Origin-Realm, (if osi) Origin-State-Id AVPS at the end of the message */
int fd_msg_add_origin ( struct msg * msg, int osi ); 
Sebastien Decugis's avatar
Sebastien Decugis committed
427

428
429
/* Parse a message against our dictionary, and in case of error log and eventually build the error reply (on return and EBADMSG, *msg == NULL or *msg is the error message ready to send) */
int fd_msg_parse_or_error( struct msg ** msg );
Sebastien Decugis's avatar
Sebastien Decugis committed
430

431
432
433
434
435
436
437
438
439
440
441

/***************************************/
/*   Dispatch module, daemon's part    */
/***************************************/

/*
 * FUNCTION:	fd_disp_app_support
 *
 * PARAMETERS:
 *  app		: The dictionary object corresponding to the Application.
 *  vendor	: (Optional) the dictionary object of a Vendor to claim support in Vendor-Specific-Application-Id
442
443
 *  auth	: Support auth app part.
 *  acct	: Support acct app part.
444
445
446
447
448
449
450
451
452
453
 *
 * DESCRIPTION: 
 *   Registers an application to be advertized in CER/CEA exchanges.
 *  Messages with an application-id matching a registered value are passed to the dispatch module,
 * while other messages are simply relayed or an error is returned (if local node does not relay)
 *
 * RETURN VALUE:
 *  0      	: The application support is registered.
 *  EINVAL 	: A parameter is invalid.
 */
454
int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct );
455
456
457
458

/* Note: if we want to support capabilities updates, we'll have to add possibility to remove an app as well... */


459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
/***************************************/
/*          Routing module             */
/***************************************/

/* This file contains the definitions of types and functions involved in the routing decisions in freeDiameter,
 * and that can be called by extensions. 
 *
 * Three different type of messages must be distinguished:
 *  - Messages received, and the peer is final recipient (IN messages)
 *  - Messages received, and the peer is not final recipient (FWD messages)
 *  - Message is locally generated (OUT messages)
 *
 * There are three global message queues (in queues.c) and also peers-specific queues (in struct fd_peer).
 *
 * (*) IN messages processing details:
 *   - the message is received from the remote peer, a FDEVP_CNX_MSG_RECV event is generated for the peer.
 *   - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable)
 *   - routable messages are queued in the fd_g_incoming global queue.
 *   - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded, 
478
 *       based on local capabilities (registered by extensions with fd_disp_app_support).
479
480
481
482
483
 *   - If the message is handled locally, it is queued in fd_g_local.
 *   - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h).
 *
 * (*) FWD messages details:
 *   - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally.
484
 *   - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned.
485
486
487
488
489
490
 *   - All callbacks registered with fd_rt_fwd_register are called for the message (see bellow).
 *     - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon.
 *   - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue.
 *   - The remaining processing is the same as for OUT messages, as described bellow.
 *
 * (*) OUT messages details:
491
492
 *   - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.)
 *   - The (routing-out) thread builds a list of possible destinations for the message, as follow:
493
494
495
 *      - create a list of all known peers in the "OPEN" state.
 *      - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops.
 *      - remove also all peers that have previously replied an error message for this message.
496
 *   - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply.
497
498
499
500
 *   - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message.
 *   - Order the resulting list of peers by score (see bellow), and sent the message to the peer with highest (positive) score.
 *    - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list.
 *      - if no peer is in OPEN state anymore, restart the process of creating the list.
501
 *   - Once a peer has been selected, the message is queued into that peer's outgoing queue.
502
 *
503
 * The following functions allow an extension to register or remove a callback as described above.
504
505
 */

506
507
508
509
510
511
512
513
/********** Forwarding callbacks: for Proxy operations ***********/

/* Handle to registered callback */
struct fd_rt_fwd_hdl;

/* Message direction for the callback */
enum fd_rt_fwd_dir {
	RT_FWD_REQ = 1,	/* The callback will be called on forwarded requests only */
Sebastien Decugis's avatar
Sebastien Decugis committed
514
515
	RT_FWD_ALL = 2,	/* The callback will be called on all forwarded messages (requests and answers )*/
	RT_FWD_ANS = 3	/* The callback will be called on answers and errors only */
516
517
518
519
520
521
522
523
524
525
526
527
528
529
};	

/*
 * FUNCTION:	fd_rt_fwd_register
 *
 * PARAMETERS:
 *  rt_fwd_cb	  : The callback function to register (see prototype bellow).
 *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
 *  dir           : One of the RT_FWD_* directions defined above.
 *  handler       : On success, a handler to the registered callback is stored here. 
 *		   This handler will be used to unregister the cb.
 *
 * DESCRIPTION: 
 *   Register a new callback for forwarded messages. See explanations above. 
Sebastien Decugis's avatar
Sebastien Decugis committed
530
 * Note that there is no guaranteed order for the callbacks calls.
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
 *
 * RETURN VALUE:
 *  0      	: The callback is registered.
 *  EINVAL 	: A parameter is invalid.
 *  ENOMEM	: Not enough memory to complete the operation
 */
int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler );
/*
 * CALLBACK:	rt_fwd_cb
 *
 * PARAMETERS:
 *  data	: pointer to some data that was passed when the callback was registered (optional).
 *  msg 	: The message that is being forwarded.
 *
 * DESCRIPTION: 
 *   This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP.
 *  The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL
 *  and handle it differently. In such case, the forwarding thread will stop processing this message.
 *
 * RETURN VALUE:
 *  0      	: Operation complete.
 *  !0 		: An error occurred -- will result in daemon's termination.
 */

/*
 * FUNCTION:	fd_rt_fwd_unregister
 *
 * PARAMETERS:
 *  handler     : The handler of the callback that must be unregistered.
 *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
 *
 * DESCRIPTION: 
 *   Removes a callback from the list of registered callbacks.
 *
 * RETURN VALUE:
 *  0      	: The callback is unregistered.
 *  EINVAL 	: A parameter is invalid.
 */
int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata );


/********** Out callbacks: for next hop routing decision operations ***********/

/* Handle to registered callback */
struct fd_rt_out_hdl;

enum fd_rt_out_score {
	FD_SCORE_NO_DELIVERY	 = -70,	/* We should not send this message to this candidate */
579
	FD_SCORE_INI		 =  -2, /* All candidates are initialized with this value */
580
581
582
	FD_SCORE_LOAD_BALANCE	 =   1,	/* Use this to differentiate between several peers with the same score */
	FD_SCORE_DEFAULT	 =   5,	/* The peer is a default route for all messages */
	FD_SCORE_DEFAULT_REALM	 =  10,	/* The peer is a default route for this realm */
583
	FD_SCORE_REALM		 =  15,	/* The peer belongs to Destination-Realm of the message */
584
585
586
587
588
589
	FD_SCORE_REDIR_HOST	 =  25,	/* If there is a redirect rule with ALL_HOST for these message and peer */
	FD_SCORE_REDIR_APP	 =  30,	/* If there is a redirect rule with ALL_APPLICATION for these message and peer */
	FD_SCORE_REDIR_REALM	 =  35,	/* If there is a redirect rule with ALL_REALM for these message and peer */
	FD_SCORE_REDIR_REALM_APP =  40,	/* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */
	FD_SCORE_REDIR_USER	 =  45,	/* If there is a redirect rule with ALL_USER for these message and peer */
	FD_SCORE_REDIR_SESSION	 =  50,	/* If there is a redirect rule with ALL_SESSION for these message and peer */
590
	FD_SCORE_FINALDEST	 = 100	/* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
};

/*
 * FUNCTION:	fd_rt_out_register
 *
 * PARAMETERS:
 *  rt_out_cb	  : The callback function to register (see prototype bellow).
 *  cbdata	  : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
 *  priority      : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner).
 *  handler       : On success, a handler to the registered callback is stored here. 
 *		   This handler will be used to unregister the cb.
 *
 * DESCRIPTION: 
 *   Register a new callback to handle OUT routing decisions. See explanations above. 
 *
 * RETURN VALUE:
 *  0      	: The callback is registered.
 *  EINVAL 	: A parameter is invalid.
 *  ENOMEM	: Not enough memory to complete the operation
 */
int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler );
/*
 * CALLBACK:	rt_out_cb
 *
 * PARAMETERS:
 *  cbdata	: pointer to some data that was registered with the callback.
 *  msg 	: The message that must be sent.
 *  list        : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract
 *
 * DESCRIPTION: 
 *   This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list.
 *  Once all registered callbacks have been called, the message is sent to the candidate with the highest score.
 *  Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it!
 *  Note also that this callback must be re-entrant since it may be called by several threads at the same time 
 *  (for different messages)
 *
 * RETURN VALUE:
 *  0      	: Operation complete.
 *  !0 		: An error occurred.
 */

/*
 * FUNCTION:	fd_rt_out_unregister
 *
 * PARAMETERS:
 *  handler     : The handler of the callback that must be unregistered.
 *  cbdata	: Will receive the data registered with the callback, that can be freed if needed.
 *
 * DESCRIPTION: 
 *   Removes a callback from the list of registered callbacks.
 *
 * RETURN VALUE:
 *  0      	: The callback is unregistered.
 *  EINVAL 	: A parameter is invalid.
 */
int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata );
647
648


Sebastien Decugis's avatar
Sebastien Decugis committed
649
650
651
652
653
654
655
656
657
658
/***************************************/
/*   Events helpers                    */
/***************************************/

struct fd_event {
	int	 code; /* codespace depends on the queue */
	size_t 	 size;
	void    *data;
};

659
/* Daemon's codespace: 1000->1999 (1500->1999 defined in fD.h) */
Sebastien Decugis's avatar
Sebastien Decugis committed
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
enum {
	 FDEV_TERMINATE	= 1000	/* request to terminate */
	,FDEV_DUMP_DICT		/* Dump the content of the dictionary */
	,FDEV_DUMP_EXT		/* Dump state of extensions */
	,FDEV_DUMP_SERV		/* Dump the server socket status */
	,FDEV_DUMP_QUEUES	/* Dump the message queues */
	,FDEV_DUMP_CONFIG	/* Dump the configuration */
	,FDEV_DUMP_PEERS	/* Dump the list of peers */
};

int fd_event_send(struct fifo *queue, int code, size_t datasz, void * data);
int fd_event_get(struct fifo *queue, int *code, size_t *datasz, void ** data);
int fd_event_timedget(struct fifo *queue, struct timespec * timeout, int timeoutcode, int *code, size_t *datasz, void ** data);
void fd_event_destroy(struct fifo **queue, void (*free_cb)(void * data));
const char * fd_ev_str(int event);


677
678
679
680
/***************************************/
/*   Endpoints lists helpers           */
/***************************************/

Sebastien Decugis's avatar
Sebastien Decugis committed
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
struct fd_endpoint {
	struct fd_list  chain;	/* link in cnf_endpoints list */
	
	union {
		sSS		ss;	/* the socket information. List is always ordered by ss value (memcmp) -- see fd_ep_add_merge */
		sSA4		sin;
		sSA6		sin6;
		sSA		sa;
	};
	
#define	EP_FL_CONF	(1 << 0)	/* This endpoint is statically configured in a configuration file */
#define	EP_FL_DISC	(1 << 1)	/* This endpoint was resolved from the Diameter Identity or other DNS query */
#define	EP_FL_ADV	(1 << 2)	/* This endpoint was advertized in Diameter CER/CEA exchange */
#define	EP_FL_LL	(1 << 3)	/* Lower layer mechanism provided this endpoint */
#define	EP_FL_PRIMARY	(1 << 4)	/* This endpoint is primary in a multihomed SCTP association */
Sebastien Decugis's avatar
Sebastien Decugis committed
696
#define	EP_ACCEPTALL	(1 << 15)	/* This flag allows bypassing the address filter in fd_ep_add_merge. */
Sebastien Decugis's avatar
Sebastien Decugis committed
697
698
699
700
701
	uint32_t	flags;		/* Additional information about the endpoint */
		
	/* To add: a validity timestamp for DNS records ? How do we retrieve this lifetime from DNS ? */
};

702
703
int fd_ep_add_merge( struct fd_list * list, sSA * sa, socklen_t sl, uint32_t flags );
int fd_ep_filter( struct fd_list * list, uint32_t flags );
704
int fd_ep_filter_family( struct fd_list * list, int af );
705
int fd_ep_clearflags( struct fd_list * list, uint32_t flags );
Sebastien Decugis's avatar
Sebastien Decugis committed
706
707
void fd_ep_dump_one( char * prefix, struct fd_endpoint * ep, char * suffix );
void fd_ep_dump( int indent, struct fd_list * eps );
708

709
710
711
712
/***************************************/
/*   Applications lists helpers        */
/***************************************/

Sebastien Decugis's avatar
Sebastien Decugis committed
713
714
715
716
717
718
719
720
721
722
struct fd_app {
	struct fd_list	 chain;	/* link in cnf_apps list. List ordered by appid. */
	struct {
		unsigned auth   : 1;
		unsigned acct   : 1;
	}		 flags;
	vendor_id_t	 vndid; /* if not 0, Vendor-Specific-App-Id AVP will be used */
	application_id_t appid;	/* The identifier of the application */
};
	
723
int fd_app_merge(struct fd_list * list, application_id_t aid, vendor_id_t vid, int auth, int acct);
Sebastien Decugis's avatar
Backup    
Sebastien Decugis committed
724
int fd_app_check(struct fd_list * list, application_id_t aid, struct fd_app **detail);
725
int fd_app_check_common(struct fd_list * list1, struct fd_list * list2, int * common_found);
726

Sebastien Decugis's avatar
Sebastien Decugis committed
727
#endif /* _FREEDIAMETER_H */