peers.c 20.4 KB
Newer Older
1
2
/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
Sebastien Decugis's avatar
Sebastien Decugis committed
3
* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
4
*													 *
5
* Copyright (c) 2011, WIDE Project and NICT								 *
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
* 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.								 *
*********************************************************************************************************/

Sebastien Decugis's avatar
Sebastien Decugis committed
36
#include "fdcore-internal.h"
37

Sebastien Decugis's avatar
Sebastien Decugis committed
38
/* Global list of peers */
39
40
struct fd_list   fd_g_peers = FD_LIST_INITIALIZER(fd_g_peers);
pthread_rwlock_t fd_g_peers_rw = PTHREAD_RWLOCK_INITIALIZER;
41

Sebastien Decugis's avatar
Sebastien Decugis committed
42
43
44
45
46
47
48
49
/* List of active peers */
struct fd_list   fd_g_activ_peers = FD_LIST_INITIALIZER(fd_g_activ_peers);	/* peers linked by their p_actives oredered by p_diamid */
pthread_rwlock_t fd_g_activ_peers_rw = PTHREAD_RWLOCK_INITIALIZER;

/* List of validation callbacks (registered with fd_peer_validate_register) */
static struct fd_list validators = FD_LIST_INITIALIZER(validators);	/* list items are simple fd_list with "o" pointing to the callback */
static pthread_rwlock_t validators_rw = PTHREAD_RWLOCK_INITIALIZER;

50

51
/* Alloc / reinit a peer structure. if *ptr is not NULL, it must already point to a valid struct fd_peer. */
Sebastien Decugis's avatar
Sebastien Decugis committed
52
int fd_peer_alloc(struct fd_peer ** ptr)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
{
	struct fd_peer *p;
	
	TRACE_ENTRY("%p", ptr);
	CHECK_PARAMS(ptr);
	
	if (*ptr) {
		p = *ptr;
	} else {
		CHECK_MALLOC( p = malloc(sizeof(struct fd_peer)) );
		*ptr = p;
	}
	
	/* Now initialize the content */
	memset(p, 0, sizeof(struct fd_peer));
	
	fd_list_init(&p->p_hdr.chain, p);
	
71
72
	fd_list_init(&p->p_hdr.info.pi_endpoints, p);
	fd_list_init(&p->p_hdr.info.runtime.pir_apps, p);
73
74
	
	p->p_eyec = EYEC_PEER;
75
76
	CHECK_POSIX( pthread_mutex_init(&p->p_state_mtx, NULL) );
	
77
	fd_list_init(&p->p_actives, p);
78
	fd_list_init(&p->p_expiry, p);
79
	CHECK_FCT( fd_fifo_new(&p->p_tosend) );
80
81
	p->p_hbh = lrand48();
	
82
	fd_list_init(&p->p_sr.srs, p);
83
	fd_list_init(&p->p_sr.exp, p);
84
	CHECK_POSIX( pthread_mutex_init(&p->p_sr.mtx, NULL) );
85
	CHECK_POSIX( pthread_cond_init(&p->p_sr.cnd, NULL) );
86
	
Sebastien Decugis's avatar
Sebastien Decugis committed
87
88
	fd_list_init(&p->p_connparams, p);
	
89
90
91
	return 0;
}

Sebastien Decugis's avatar
Sebastien Decugis committed
92
93
94
95
/* Add a new peer entry */
int fd_peer_add ( struct peer_info * info, char * orig_dbg, void (*cb)(struct peer_info *, void *), void * cb_data )
{
	struct fd_peer *p = NULL;
96
	struct fd_list * li, *li_inf;
Sebastien Decugis's avatar
Sebastien Decugis committed
97
	int ret = 0;
98
	
Sebastien Decugis's avatar
Sebastien Decugis committed
99
100
101
	TRACE_ENTRY("%p %p %p %p", info, orig_dbg, cb, cb_data);
	CHECK_PARAMS(info && info->pi_diamid);
	
102
103
104
105
106
107
108
	if (info->config.pic_realm) {
		if (!fd_os_is_valid_DiameterIdentity((os0_t)info->config.pic_realm, strlen(info->config.pic_realm))) {
			TRACE_DEBUG(INFO, "'%s' is not a valid DiameterIdentity.", info->config.pic_realm);
			return EINVAL;
		}
	}
	
Sebastien Decugis's avatar
Sebastien Decugis committed
109
110
111
112
	/* Create a structure to contain the new peer information */
	CHECK_FCT( fd_peer_alloc(&p) );
	
	/* Copy the informations from the parameters received */
113
114
	p->p_hdr.info.pi_diamid = info->pi_diamid;
	CHECK_FCT( fd_os_validate_DiameterIdentity(&p->p_hdr.info.pi_diamid, &p->p_hdr.info.pi_diamidlen, 1) );
Sebastien Decugis's avatar
Sebastien Decugis committed
115
	
116
	memcpy( &p->p_hdr.info.config, &info->config, sizeof(p->p_hdr.info.config) );
117
	
118
119
120
121
122
	/* Duplicate the strings if provided */
	if (info->config.pic_realm) {
		CHECK_MALLOC( p->p_hdr.info.config.pic_realm = strdup(info->config.pic_realm) );
	}
	if (info->config.pic_priority) {
123
		CHECK_MALLOC( p->p_hdr.info.config.pic_priority = strdup(info->config.pic_priority) );
Sebastien Decugis's avatar
Sebastien Decugis committed
124
125
	}
	
126
	/* Move the list of endpoints into the peer */
Sebastien Decugis's avatar
Sebastien Decugis committed
127
128
129
130
131
132
133
134
135
136
137
	if (info->pi_endpoints.next)
		while (!FD_IS_LIST_EMPTY( &info->pi_endpoints ) ) {
			li = info->pi_endpoints.next;
			fd_list_unlink(li);
			fd_list_insert_before(&p->p_hdr.info.pi_endpoints, li);
		}
	
	/* The internal data */
	if (orig_dbg) {
		CHECK_MALLOC( p->p_dbgorig = strdup(orig_dbg) );
	} else {
138
		CHECK_MALLOC( p->p_dbgorig = strdup("unspecified") );
Sebastien Decugis's avatar
Sebastien Decugis committed
139
140
141
142
143
144
	}
	p->p_cb = cb;
	p->p_cb_data = cb_data;
	
	/* Ok, now check if we don't already have an entry with the same Diameter Id, and insert this one */
	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
145
	li_inf = &fd_g_peers;
Sebastien Decugis's avatar
Sebastien Decugis committed
146
	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
147
		struct fd_peer * next = (struct fd_peer *)li;
148
149
150
151
		int cont;
		int cmp = fd_os_almostcasesrch( p->p_hdr.info.pi_diamid, p->p_hdr.info.pi_diamidlen, 
						next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen,
						&cont );
Sebastien Decugis's avatar
Sebastien Decugis committed
152
153
		if (cmp > 0)
			li_inf = li; /* it will come after this element, for sure */
154
155
		
		if (cmp == 0) {
Sebastien Decugis's avatar
Sebastien Decugis committed
156
			ret = EEXIST; /* we have a duplicate */
157
158
159
160
			break;
		}
		if (!cont)
			break;
Sebastien Decugis's avatar
Sebastien Decugis committed
161
162
163
	}
	
	/* We can insert the new peer object */
164
165
166
167
168
169
	if (! ret)
		do {
			/* Update expiry list */
			CHECK_FCT_DO( ret = fd_p_expi_update( p ), break );

			/* Insert the new element in the list */
170
			fd_list_insert_after( li_inf, &p->p_hdr.chain );
171
		} while (0);
Sebastien Decugis's avatar
Sebastien Decugis committed
172
173
174
175
176
177
178
179
180
181

	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
	if (ret) {
		CHECK_FCT( fd_peer_free(&p) );
	} else {
		CHECK_FCT( fd_psm_begin(p) );
	}
	return ret;
}

Sebastien Decugis's avatar
Sebastien Decugis committed
182
/* Search for a peer */
183
int fd_peer_getbyid( DiamId_t diamid, size_t diamidlen, int igncase, struct peer_hdr ** peer )
Sebastien Decugis's avatar
Sebastien Decugis committed
184
185
{
	struct fd_list * li;
186
187
	TRACE_ENTRY("%p %zd %d %p", diamid, diamidlen, igncase, peer);
	CHECK_PARAMS( diamid && diamidlen && peer );
Sebastien Decugis's avatar
Sebastien Decugis committed
188
189
190
191
192
	
	*peer = NULL;
	
	/* Search in the list */
	CHECK_POSIX( pthread_rwlock_rdlock(&fd_g_peers_rw) );
Sebastien Decugis's avatar
Sebastien Decugis committed
193
	if (igncase) {
194
195
196
197
198
199
200
201
202
203
204
		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
			struct fd_peer * next = (struct fd_peer *)li;
			int cmp, cont;
			cmp = fd_os_almostcasesrch( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen, &cont );
			if (cmp == 0) {
				*peer = &next->p_hdr;
				break;
			}
			if (!cont)
				break;
		}
Sebastien Decugis's avatar
Sebastien Decugis committed
205
	} else {
206
207
208
209
210
211
212
213
214
		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
			struct fd_peer * next = (struct fd_peer *)li;
			int cmp = fd_os_cmp( diamid, diamidlen, next->p_hdr.info.pi_diamid, next->p_hdr.info.pi_diamidlen );
			if (cmp > 0)
				continue;
			if (cmp == 0)
				*peer = &next->p_hdr;
			break;
		}
Sebastien Decugis's avatar
Sebastien Decugis committed
215
	}
Sebastien Decugis's avatar
Sebastien Decugis committed
216
217
218
219
220
	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );
	
	return 0;
}

Sebastien Decugis's avatar
Sebastien Decugis committed
221

222
223
224
225
226
227
228
229
230
231
232
233
234
#define free_null( _v ) 	\
	if (_v) {		\
		free(_v);	\
		(_v) = NULL;	\
	}
	
#define free_list( _l ) 						\
	while (!FD_IS_LIST_EMPTY(_l)) {					\
		struct fd_list * __li = ((struct fd_list *)(_l))->next;	\
		fd_list_unlink(__li);					\
		free(__li);						\
	}

235
236
237
238
239
240
241
242
243
244
/* Empty the lists of p_tosend and p_sentreq messages */
void fd_peer_failover_msg(struct fd_peer * peer)
{
	struct msg *m;
	TRACE_ENTRY("%p", peer);
	CHECK_PARAMS_DO(CHECK_PEER(peer), return);
	
	/* Requeue all messages in the "out" queue */
	while ( fd_fifo_tryget(peer->p_tosend, &m) == 0 ) {
		CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &m), 
245
			{
246
				/* fallback: destroy the message */
247
248
249
				fd_msg_log(FD_MSG_LOG_DROPPED, m, "Internal error: unable to requeue this message during failover process");
				CHECK_FCT_DO(fd_msg_free(m), /* What can we do more? */)
			} );
250
251
252
253
254
255
256
257
258
	}
	
	/* Requeue all routable sent requests */
	fd_p_sr_failover(&peer->p_sr);
	
	/* Done */
	return;
}

259
/* Destroy a structure once cleanups have been performed (fd_psm_abord, ...) */
Sebastien Decugis's avatar
Sebastien Decugis committed
260
int fd_peer_free(struct fd_peer ** ptr)
261
262
263
264
265
266
267
268
269
{
	struct fd_peer *p;
	
	TRACE_ENTRY("%p", ptr);
	CHECK_PARAMS(ptr);
	p = *ptr;
	*ptr = NULL;
	CHECK_PARAMS(p);
	
270
	CHECK_PARAMS( FD_IS_LIST_EMPTY(&p->p_hdr.chain) );
271
	
272
273
274
275
276
277
278
279
280
	free_null(p->p_hdr.info.pi_diamid);
	
	free_null(p->p_hdr.info.config.pic_realm); 
	free_null(p->p_hdr.info.config.pic_priority); 
	
	free_null(p->p_hdr.info.runtime.pir_realm);
	free_null(p->p_hdr.info.runtime.pir_prodname);
	free_list( &p->p_hdr.info.runtime.pir_apps );
	
281
282
283
284
	free_list( &p->p_hdr.info.pi_endpoints );
	
	free_null(p->p_dbgorig);
	
285
286
	fd_list_unlink(&p->p_expiry);
	fd_list_unlink(&p->p_actives);
287
	
288
	CHECK_FCT_DO( fd_fifo_del(&p->p_tosend), /* continue */ );
289
	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_state_mtx), /* continue */);
290
	CHECK_POSIX_DO( pthread_mutex_destroy(&p->p_sr.mtx), /* continue */);
291
	CHECK_POSIX_DO( pthread_cond_destroy(&p->p_sr.cnd), /* continue */);
292
293
	
	/* If the callback is still around... */
294
295
296
	if (p->p_cb)
		(*p->p_cb)(NULL, p->p_cb_data);
	
297
	/* Free the structure */
298
299
300
301
	free(p);
	return 0;
}

302
/* Terminate peer module (destroy all peers, first gently, then violently) */
Sebastien Decugis's avatar
Sebastien Decugis committed
303
int fd_peer_fini()
304
{
305
	struct fd_list * li;
Sebastien Decugis's avatar
Sebastien Decugis committed
306
307
308
	struct fd_list purge = FD_LIST_INITIALIZER(purge); /* Store zombie peers here */
	int list_empty;
	struct timespec	wait_until, now;
309
	
Sebastien Decugis's avatar
Sebastien Decugis committed
310
	TRACE_ENTRY();
311
	
Sebastien Decugis's avatar
Sebastien Decugis committed
312
	CHECK_FCT_DO(fd_p_expi_fini(), /* continue */);
313
	
Sebastien Decugis's avatar
Sebastien Decugis committed
314
	TRACE_DEBUG(INFO, "Sending terminate signal to all peer connections");
315
	
Sebastien Decugis's avatar
Sebastien Decugis committed
316
317
	CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
318
		struct fd_peer * peer = (struct fd_peer *)li->o;
Sebastien Decugis's avatar
Sebastien Decugis committed
319
		
320
		if (fd_peer_getstate(peer) != STATE_ZOMBIE) {
Sebastien Decugis's avatar
Sebastien Decugis committed
321
			CHECK_FCT_DO( fd_psm_terminate(peer, "REBOOTING"), /* continue */ );
Sebastien Decugis's avatar
Sebastien Decugis committed
322
323
324
325
		} else {
			li = li->prev; /* to avoid breaking the loop */
			fd_list_unlink(&peer->p_hdr.chain);
			fd_list_insert_before(&purge, &peer->p_hdr.chain);
326
		}
Sebastien Decugis's avatar
Sebastien Decugis committed
327
328
329
	}
	list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
330
	
Sebastien Decugis's avatar
Sebastien Decugis committed
331
332
	if (!list_empty) {
		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
333
334
		TRACE_DEBUG(INFO, "Waiting for connections shutdown... (%d sec max)", DPR_TIMEOUT + 1);
		wait_until.tv_sec  = now.tv_sec + DPR_TIMEOUT + 1;
Sebastien Decugis's avatar
Sebastien Decugis committed
335
336
		wait_until.tv_nsec = now.tv_nsec;
	}
337
	
Sebastien Decugis's avatar
Sebastien Decugis committed
338
339
340
	while ((!list_empty) && (TS_IS_INFERIOR(&now, &wait_until))) {
		
		/* Allow the PSM(s) to execute */
341
		usleep(100000);
Sebastien Decugis's avatar
Sebastien Decugis committed
342
343
344
345
		
		/* Remove zombie peers */
		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
		for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
346
347
			struct fd_peer * peer = (struct fd_peer *)li->o;
			if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
Sebastien Decugis's avatar
Sebastien Decugis committed
348
349
350
351
352
353
354
				li = li->prev; /* to avoid breaking the loop */
				fd_list_unlink(&peer->p_hdr.chain);
				fd_list_insert_before(&purge, &peer->p_hdr.chain);
			}
		}
		list_empty = FD_IS_LIST_EMPTY(&fd_g_peers);
		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
Sebastien Decugis's avatar
Sebastien Decugis committed
355
		CHECK_SYS(  clock_gettime(CLOCK_REALTIME, &now)  );
356
357
	}
	
Sebastien Decugis's avatar
Sebastien Decugis committed
358
359
360
361
	if (!list_empty) {
		TRACE_DEBUG(INFO, "Forcing connections shutdown");
		CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_peers_rw), /* continue */ );
		while (!FD_IS_LIST_EMPTY(&fd_g_peers)) {
362
			struct fd_peer * peer = (struct fd_peer *)(fd_g_peers.next->o);
Sebastien Decugis's avatar
Sebastien Decugis committed
363
364
365
366
367
368
			fd_psm_abord(peer);
			fd_list_unlink(&peer->p_hdr.chain);
			fd_list_insert_before(&purge, &peer->p_hdr.chain);
		}
		CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
	}
369
	
Sebastien Decugis's avatar
Sebastien Decugis committed
370
371
	/* Free memory objects of all peers */
	while (!FD_IS_LIST_EMPTY(&purge)) {
372
		struct fd_peer * peer = (struct fd_peer *)(purge.next->o);
Sebastien Decugis's avatar
Sebastien Decugis committed
373
374
		fd_list_unlink(&peer->p_hdr.chain);
		fd_peer_free(&peer);
375
376
	}
	
Sebastien Decugis's avatar
Sebastien Decugis committed
377
378
379
380
381
382
383
384
385
	/* Now empty the validators list */
	CHECK_FCT_DO( pthread_rwlock_wrlock(&validators_rw), /* continue */ );
	while (!FD_IS_LIST_EMPTY( &validators )) {
		struct fd_list * v = validators.next;
		fd_list_unlink(v);
		free(v);
	}
	CHECK_FCT_DO( pthread_rwlock_unlock(&validators_rw), /* continue */ );
	
Sebastien Decugis's avatar
Sebastien Decugis committed
386
387
388
389
390
391
392
393
394
395
396
	return 0;
}

/* Dump info of one peer */
void fd_peer_dump(struct fd_peer * peer, int details)
{
	if (peer->p_eyec != EYEC_PEER) {
		fd_log_debug("  Invalid peer @ %p !\n", peer);
		return;
	}

397
	fd_log_debug(">  %s\t%s", STATE_STR(fd_peer_getstate(peer)), peer->p_hdr.info.pi_diamid);
Sebastien Decugis's avatar
Sebastien Decugis committed
398
	if (details > INFO) {
399
		fd_log_debug("\t(rlm:%s)", peer->p_hdr.info.runtime.pir_realm ?: "<unknown>");
400
401
		if (peer->p_hdr.info.runtime.pir_prodname)
			fd_log_debug("\t['%s' %u]", peer->p_hdr.info.runtime.pir_prodname, peer->p_hdr.info.runtime.pir_firmrev);
Sebastien Decugis's avatar
Sebastien Decugis committed
402
403
404
405
	}
	fd_log_debug("\n");
	if (details > FULL) {
		/* Dump all info */
406
407
408
409
410
411
412
413
414
415
416
		fd_log_debug("\tEntry origin : %s\n", peer->p_dbgorig?: "not set");
		fd_log_debug("\tConfig flags : %s%s%s%s%s - %s%s%s\n", 
				peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_DEFAULT ? "" :
					(peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP ? "IP." : "IPv6."),
				peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_DEFAULT ? "" :
					(peer->p_hdr.info.config.pic_flags.pro4 == PI_P4_TCP ? "TCP." : "SCTP."),
				peer->p_hdr.info.config.pic_flags.alg ? "PrefTCP." : "",
				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE ? "NoTLSok" :"",
				peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD ? "OldTLS" :"",
				peer->p_hdr.info.config.pic_flags.exp ? "Expire." : "",
				peer->p_hdr.info.config.pic_flags.persist ? "Persist." : ""
Sebastien Decugis's avatar
Sebastien Decugis committed
417
				);
418
		fd_log_debug("\tLifetime : %d sec\n", peer->p_hdr.info.config.pic_lft);
419
	}
Sebastien Decugis's avatar
Sebastien Decugis committed
420
}
421

Sebastien Decugis's avatar
Sebastien Decugis committed
422
423
424
425
426
427
428
429
430
/* Dump the list of peers */
void fd_peer_dump_list(int details)
{
	struct fd_list * li;
	
	fd_log_debug("Dumping list of peers :\n");
	CHECK_FCT_DO( pthread_rwlock_rdlock(&fd_g_peers_rw), /* continue */ );
	
	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
431
		struct fd_peer * np = (struct fd_peer *)li->o;
Sebastien Decugis's avatar
Sebastien Decugis committed
432
		fd_peer_dump(np, details);
433
	}
Sebastien Decugis's avatar
Sebastien Decugis committed
434
435
	
	CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_peers_rw), /* continue */ );
436
}
Sebastien Decugis's avatar
Sebastien Decugis committed
437

438
439
440
static struct dict_object *avp_oh_model = NULL;
static pthread_mutex_t cache_avp_lock = PTHREAD_MUTEX_INITIALIZER;

441
/* Handle an incoming CER request on a new connection */
442
int fd_peer_handle_newCER( struct msg ** cer, struct cnxctx ** cnx )
443
444
445
446
{
	struct msg * msg;
	struct avp *avp_oh;
	struct avp_hdr * avp_hdr;
447
	struct fd_list * li, *li_inf;
448
	int found = 0;
449
	int ret = 0;
450
	struct fd_peer * peer;
451
	struct cnx_incoming * ev_data;
452
	
453
	TRACE_ENTRY("%p %p", cer, cnx);
454
455
456
457
	CHECK_PARAMS(cer && *cer && cnx && *cnx);
	
	msg = *cer; 
	
458
	/* If needed, resolve the dictionary model for Origin-Host */
459
460
461
462
463
464
465
466
467
	CHECK_POSIX( pthread_mutex_lock(&cache_avp_lock) );
	if (!avp_oh_model) {
		avp_code_t code = AC_ORIGIN_HOST;
		int ret;
		CHECK_FCT_DO( ret = fd_dict_search ( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE, &code, &avp_oh_model, ENOENT),
			{ CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) ); return ret; } );
	}
	CHECK_POSIX( pthread_mutex_unlock(&cache_avp_lock) );
	
468
469
	/* Find the Diameter Identity of the remote peer in the message */
	CHECK_FCT( fd_msg_search_avp ( msg, avp_oh_model, &avp_oh ) );
470
	ASSERT(avp_oh); /* otherwise it should not have passed rules validation, right? */
471
472
	CHECK_FCT( fd_msg_avp_hdr ( avp_oh, &avp_hdr ) );
	
473
	/* First, check if the Origin-Host value is valid */
474
	if (!fd_os_is_valid_DiameterIdentity(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len)) {
475
		TRACE_DEBUG(INFO, "Received new CER with invalid Origin-Host");
476
477
478
479
480
481
482
		CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ) );
		CHECK_FCT( fd_msg_rescode_set(*cer, "ER_DIAMETER_INVALID_AVP_VALUE", 
							"Your Origin-Host contains invalid characters.", avp_oh, 1 ) );
		CHECK_FCT( fd_out_send(cer, *cnx, NULL, FD_CNX_ORDERED) );
		return EINVAL;
	}
	
483
484
485
486
	/* Search if we already have this peer id in our list. We take directly the write lock so that we don't need to upgrade if it is a new peer.
	 * There is space for a small optimization here if needed.
	 */
	CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_peers_rw) );
487
	
488
	li_inf = &fd_g_peers;
489
	for (li = fd_g_peers.next; li != &fd_g_peers; li = li->next) {
490
		int cmp, cont;
491
		peer = (struct fd_peer *)li;
492
		cmp = fd_os_almostcasesrch( avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len, peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, &cont );
493
		if (cmp > 0) {
494
495
496
			li_inf = li;
		}
		if (cmp == 0) {
497
			found = 1;
498
499
500
501
			break;
		}
		if (!cont)
			break;
502
503
504
	}
	
	if (!found) {
505
506
507
508
509
		/* Create a new peer entry for this new remote peer */
		peer = NULL;
		CHECK_FCT_DO( ret = fd_peer_alloc(&peer), goto out );
		
		/* Set the peer Diameter Id and the responder flag parameters */
510
511
512
		CHECK_MALLOC_DO( peer->p_hdr.info.pi_diamid = os0dup(avp_hdr->avp_value->os.data, avp_hdr->avp_value->os.len), 
			{ ret = ENOMEM; goto out; } );
		peer->p_hdr.info.pi_diamidlen = avp_hdr->avp_value->os.len;
513
514
		CHECK_MALLOC_DO( peer->p_dbgorig = strdup(fd_cnx_getid(*cnx)), { ret = ENOMEM; goto out; } );
		peer->p_flags.pf_responder = 1;
Sebastien Decugis's avatar
Sebastien Decugis committed
515
		peer->p_flags.pf_delete = 1;
516
		
Sebastien Decugis's avatar
Backup    
Sebastien Decugis committed
517
		/* Set this peer to expire on inactivity */
518
		peer->p_hdr.info.config.pic_flags.exp 	= PI_EXP_INACTIVE;
Sebastien Decugis's avatar
Sebastien Decugis committed
519
520
		peer->p_hdr.info.config.pic_lft		= 3600;	/* 1 hour without any message 
		-- RFC3539 states that this must not be inferior to BRINGDOWN_INTERVAL = 5 minutes */
521
522
		CHECK_FCT_DO( ret = fd_p_expi_update( peer ), goto out );

Sebastien Decugis's avatar
Backup    
Sebastien Decugis committed
523
		
524
		/* Insert the new peer in the list (the PSM will take care of setting the expiry after validation) */
525
		fd_list_insert_after( li_inf, &peer->p_hdr.chain );
526
		
527
528
		/* Start the PSM, which will receive the event bellow */
		CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
529
530
	} else {
		/* Check if the peer is in zombie state */
531
		if (fd_peer_getstate(peer) == STATE_ZOMBIE) {
532
			/* Re-activate the peer */
533
534
			if (peer->p_hdr.info.config.pic_flags.exp)
				peer->p_flags.pf_responder = 1;
535
536
537
538
			CHECK_POSIX_DO( pthread_mutex_lock(&peer->p_state_mtx), );
			peer->p_state = STATE_NEW;
			CHECK_POSIX_DO( pthread_mutex_unlock(&peer->p_state_mtx), );
			peer->p_flags.pf_localterm = 0;
539
540
			CHECK_FCT_DO( ret = fd_psm_begin(peer), goto out );
		}
541
	}
542
	
543
544
545
546
547
548
549
	/* Send the new connection event to the PSM */
	CHECK_MALLOC_DO( ev_data = malloc(sizeof(struct cnx_incoming)), { ret = ENOMEM; goto out; } );
	memset(ev_data, 0, sizeof(ev_data));
	
	ev_data->cer = msg;
	ev_data->cnx = *cnx;
	ev_data->validate = !found;
550
	
551
	CHECK_FCT_DO( ret = fd_event_send(peer->p_events, FDEVP_CNX_INCOMING, sizeof(ev_data), ev_data), goto out );
552
	
553
out:	
554
555
	CHECK_POSIX( pthread_rwlock_unlock(&fd_g_peers_rw) );

556
557
558
559
560
	if (ret == 0) {
		/* Reset the "out" parameters, so that they are not cleanup on function return. */
		*cer = NULL;
		*cnx = NULL;
	}
561
	
562
	return ret;
563
564
565
566
567
}

/* Save a callback to accept / reject incoming unknown peers */
int fd_peer_validate_register ( int (*peer_validate)(struct peer_info * /* info */, int * /* auth */, int (**cb2)(struct peer_info *)) )
{
Sebastien Decugis's avatar
Sebastien Decugis committed
568
569
570
571
	struct fd_list * v;
	
	TRACE_ENTRY("%p", peer_validate);
	CHECK_PARAMS(peer_validate);
572
	
Sebastien Decugis's avatar
Sebastien Decugis committed
573
574
575
576
577
578
579
580
581
582
583
	/* Alloc a new entry */
	CHECK_MALLOC( v = malloc(sizeof(struct fd_list)) );
	fd_list_init( v, peer_validate );
	
	/* Add at the beginning of the list */
	CHECK_FCT( pthread_rwlock_wrlock(&validators_rw) );
	fd_list_insert_after(&validators, v);
	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
	
	/* Done! */
	return 0;
584
}
585

Sebastien Decugis's avatar
Sebastien Decugis committed
586
/* Validate a peer by calling the callbacks in turn -- return 0 if the peer is validated, ! 0 in case of error (>0) or if the peer is rejected (-1) */
587
588
int fd_peer_validate( struct fd_peer * peer )
{
Sebastien Decugis's avatar
Sebastien Decugis committed
589
590
591
592
593
594
595
	int ret = 0;
	struct fd_list * v;
	
	CHECK_FCT( pthread_rwlock_rdlock(&validators_rw) );
	for (v = validators.next; v != &validators; v = v->next) {
		int auth = 0;
		pthread_cleanup_push(fd_cleanup_rwlock, &validators_rw);
596
		CHECK_FCT_DO( ret = ((int(*)(struct peer_info *, int *, int (**)(struct peer_info *)))(v->o)) (&peer->p_hdr.info, &auth, &peer->p_cb2),  );
Sebastien Decugis's avatar
Sebastien Decugis committed
597
		pthread_cleanup_pop(0);
598
599
		if (ret)
			goto out;
Sebastien Decugis's avatar
Sebastien Decugis committed
600
601
602
603
604
605
606
607
608
609
610
611
		if (auth) {
			ret = (auth > 0) ? 0 : -1;
			goto out;
		}
		peer->p_cb2 = NULL;
	}
	
	/* No callback has given a firm result, the default is to reject */
	ret = -1;
out:
	CHECK_FCT( pthread_rwlock_unlock(&validators_rw));
	return ret;
612
}