Mercurial > hg > freeDiameter
comparison freeDiameter/routing_dispatch.c @ 123:960fa8048805
Merged routing and dispatch files for similarities
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 09 Dec 2009 19:02:31 +0900 |
parents | freeDiameter/routing.c@e66a82a739fa |
children | cc42d8607114 |
comparison
equal
deleted
inserted
replaced
122:e66a82a739fa | 123:960fa8048805 |
---|---|
1 /********************************************************************************************************* | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Sebastien Decugis <sdecugis@nict.go.jp> * | |
4 * * | |
5 * Copyright (c) 2009, WIDE Project and NICT * | |
6 * All rights reserved. * | |
7 * * | |
8 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
9 * permitted provided that the following conditions are met: * | |
10 * * | |
11 * * Redistributions of source code must retain the above * | |
12 * copyright notice, this list of conditions and the * | |
13 * following disclaimer. * | |
14 * * | |
15 * * Redistributions in binary form must reproduce the above * | |
16 * copyright notice, this list of conditions and the * | |
17 * following disclaimer in the documentation and/or other * | |
18 * materials provided with the distribution. * | |
19 * * | |
20 * * Neither the name of the WIDE Project or NICT nor the * | |
21 * names of its contributors may be used to endorse or * | |
22 * promote products derived from this software without * | |
23 * specific prior written permission of WIDE Project and * | |
24 * NICT. * | |
25 * * | |
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
34 *********************************************************************************************************/ | |
35 | |
36 #include "fD.h" | |
37 | |
38 /********************************************************************************/ | |
39 /* First part : handling the extensions callbacks */ | |
40 /********************************************************************************/ | |
41 | |
42 /* Lists of the callbacks, and locks to protect them */ | |
43 static pthread_rwlock_t rt_fwd_lock = PTHREAD_RWLOCK_INITIALIZER; | |
44 static struct fd_list rt_fwd_list = FD_LIST_INITIALIZER_O(rt_fwd_list, &rt_fwd_lock); | |
45 | |
46 static pthread_rwlock_t rt_out_lock = PTHREAD_RWLOCK_INITIALIZER; | |
47 static struct fd_list rt_out_list = FD_LIST_INITIALIZER_O(rt_out_list, &rt_out_lock); | |
48 | |
49 /* Items in the lists are the same */ | |
50 struct rt_hdl { | |
51 struct fd_list chain; /* link in the rt_fwd_list or rt_out_list */ | |
52 void * cbdata; /* the registered data */ | |
53 union { | |
54 int order; /* This value is used to sort the list */ | |
55 int dir; /* It is the direction for FWD handlers */ | |
56 int prio; /* and the priority for OUT handlers */ | |
57 }; | |
58 union { | |
59 int (*rt_fwd_cb)(void * cbdata, struct msg ** msg); | |
60 int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates); | |
61 }; | |
62 }; | |
63 | |
64 /* Add a new entry in the list */ | |
65 static int add_ordered(struct rt_hdl * new, struct fd_list * list) | |
66 { | |
67 /* The list is ordered by prio parameter */ | |
68 struct fd_list * li; | |
69 | |
70 CHECK_POSIX( pthread_rwlock_wrlock(list->o) ); | |
71 | |
72 for (li = list->next; li != list; li = li->next) { | |
73 struct rt_hdl * h = (struct rt_hdl *) li; | |
74 if (new->order <= h->order) | |
75 break; | |
76 } | |
77 | |
78 fd_list_insert_before(li, &new->chain); | |
79 | |
80 CHECK_POSIX( pthread_rwlock_unlock(list->o) ); | |
81 | |
82 return 0; | |
83 } | |
84 | |
85 /* Register a new FWD callback */ | |
86 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 ) | |
87 { | |
88 struct rt_hdl * new; | |
89 | |
90 TRACE_ENTRY("%p %p %d %p", rt_fwd_cb, cbdata, dir, handler); | |
91 CHECK_PARAMS( rt_fwd_cb ); | |
92 CHECK_PARAMS( (dir >= RT_FWD_REQ) && ( dir <= RT_FWD_ANS) ); | |
93 | |
94 /* Create a new container */ | |
95 CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl))); | |
96 memset(new, 0, sizeof(struct rt_hdl)); | |
97 | |
98 /* Write the content */ | |
99 fd_list_init(&new->chain, NULL); | |
100 new->cbdata = cbdata; | |
101 new->dir = dir; | |
102 new->rt_fwd_cb = rt_fwd_cb; | |
103 | |
104 /* Save this in the list */ | |
105 CHECK_FCT( add_ordered(new, &rt_fwd_list) ); | |
106 | |
107 /* Give it back to the extension if needed */ | |
108 if (handler) | |
109 *handler = (void *)new; | |
110 | |
111 return 0; | |
112 } | |
113 | |
114 /* Remove it */ | |
115 int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata ) | |
116 { | |
117 struct rt_hdl * del; | |
118 TRACE_ENTRY( "%p %p", handler, cbdata); | |
119 CHECK_PARAMS( handler ); | |
120 | |
121 del = (struct rt_hdl *)handler; | |
122 CHECK_PARAMS( del->chain.head == &rt_fwd_list ); | |
123 | |
124 /* Unlink */ | |
125 CHECK_POSIX( pthread_rwlock_wrlock(&rt_fwd_lock) ); | |
126 fd_list_unlink(&del->chain); | |
127 CHECK_POSIX( pthread_rwlock_unlock(&rt_fwd_lock) ); | |
128 | |
129 if (cbdata) | |
130 *cbdata = del->cbdata; | |
131 | |
132 free(del); | |
133 return 0; | |
134 } | |
135 | |
136 /* Register a new OUT callback */ | |
137 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 ) | |
138 { | |
139 struct rt_hdl * new; | |
140 | |
141 TRACE_ENTRY("%p %p %d %p", rt_out_cb, cbdata, priority, handler); | |
142 CHECK_PARAMS( rt_out_cb ); | |
143 | |
144 /* Create a new container */ | |
145 CHECK_MALLOC(new = malloc(sizeof(struct rt_hdl))); | |
146 memset(new, 0, sizeof(struct rt_hdl)); | |
147 | |
148 /* Write the content */ | |
149 fd_list_init(&new->chain, NULL); | |
150 new->cbdata = cbdata; | |
151 new->prio = priority; | |
152 new->rt_out_cb = rt_out_cb; | |
153 | |
154 /* Save this in the list */ | |
155 CHECK_FCT( add_ordered(new, &rt_out_list) ); | |
156 | |
157 /* Give it back to the extension if needed */ | |
158 if (handler) | |
159 *handler = (void *)new; | |
160 | |
161 return 0; | |
162 } | |
163 | |
164 /* Remove it */ | |
165 int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata ) | |
166 { | |
167 struct rt_hdl * del; | |
168 TRACE_ENTRY( "%p %p", handler, cbdata); | |
169 CHECK_PARAMS( handler ); | |
170 | |
171 del = (struct rt_hdl *)handler; | |
172 CHECK_PARAMS( del->chain.head == &rt_out_list ); | |
173 | |
174 /* Unlink */ | |
175 CHECK_POSIX( pthread_rwlock_wrlock(&rt_out_lock) ); | |
176 fd_list_unlink(&del->chain); | |
177 CHECK_POSIX( pthread_rwlock_unlock(&rt_out_lock) ); | |
178 | |
179 if (cbdata) | |
180 *cbdata = del->cbdata; | |
181 | |
182 free(del); | |
183 return 0; | |
184 } | |
185 | |
186 /********************************************************************************/ | |
187 /* Helper functions */ | |
188 /********************************************************************************/ | |
189 /* Test if a User-Name AVP contains a Decorated NAI -- RFC4282, draft-ietf-dime-nai-routing-04 */ | |
190 static int is_decorated_NAI(union avp_value * un) | |
191 { | |
192 int i; | |
193 TRACE_ENTRY("%p", un); | |
194 | |
195 /* If there was no User-Name, we return false */ | |
196 if (un == NULL) | |
197 return 0; | |
198 | |
199 /* Search if there is a '!' before any '@' -- do we need to check it contains a '.' ? */ | |
200 for (i = 0; i < un->os.len; i++) { | |
201 if ( un->os.data[i] == (unsigned char) '!' ) | |
202 return 1; | |
203 if ( un->os.data[i] == (unsigned char) '@' ) | |
204 break; | |
205 if ( un->os.data[i] == (unsigned char) '\\' ) | |
206 i++; /* next one was escaped */ | |
207 } | |
208 | |
209 return 0; | |
210 } | |
211 | |
212 /* Create new User-Name and Destination-Realm values */ | |
213 static int process_decorated_NAI(union avp_value * un, union avp_value * dr) | |
214 { | |
215 int i, at_idx = 0, sep_idx = 0; | |
216 unsigned char * old_un; | |
217 TRACE_ENTRY("%p %p", un, dr); | |
218 CHECK_PARAMS(un && dr); | |
219 | |
220 /* Save the decorated User-Name, for example 'homerealm.example.net!user@otherrealm.example.net' */ | |
221 old_un = un->os.data; | |
222 | |
223 /* Search the positions of the first '!' and the '@' in the string */ | |
224 for (i = 0; i < un->os.len; i++) { | |
225 if ( (!sep_idx) && (old_un[i] == (unsigned char) '!') ) | |
226 sep_idx = i; | |
227 if ( old_un[i] == (unsigned char) '@' ) { | |
228 at_idx = i; | |
229 break; | |
230 } | |
231 if ( un->os.data[i] == (unsigned char) '\\' ) | |
232 i++; /* next one is escaped */ | |
233 } | |
234 | |
235 CHECK_PARAMS( 0 < sep_idx < at_idx < un->os.len); | |
236 | |
237 /* Create the new User-Name value */ | |
238 CHECK_MALLOC( un->os.data = malloc( at_idx ) ); | |
239 memcpy( un->os.data, old_un + sep_idx + 1, at_idx - sep_idx ); /* user@ */ | |
240 memcpy( un->os.data + at_idx - sep_idx, old_un, sep_idx ); /* homerealm.example.net */ | |
241 | |
242 /* Create the new Destination-Realm value */ | |
243 CHECK_MALLOC( dr->os.data = realloc(dr->os.data, sep_idx) ); | |
244 memcpy( dr->os.data, old_un, sep_idx ); | |
245 dr->os.len = sep_idx; | |
246 | |
247 TRACE_DEBUG(FULL, "Processed Decorated NAI '%.*s' into '%.*s' (%.*s)", | |
248 un->os.len, old_un, | |
249 at_idx, un->os.data, | |
250 dr->os.len, dr->os.data); | |
251 | |
252 un->os.len = at_idx; | |
253 free(old_un); | |
254 | |
255 return 0; | |
256 } | |
257 | |
258 /* Function to return an error to an incoming request */ | |
259 static int return_error(struct msg * msg, char * error_code, char * error_message, struct avp * failedavp) | |
260 { | |
261 struct fd_peer * peer; | |
262 int is_loc = 0; | |
263 | |
264 /* Get the source of the message */ | |
265 { | |
266 char * id; | |
267 CHECK_FCT( fd_msg_source_get( msg, &id ) ); | |
268 | |
269 if (id == NULL) { | |
270 is_loc = 1; /* The message was issued locally */ | |
271 } else { | |
272 | |
273 /* Search the peer with this id */ | |
274 CHECK_FCT( fd_peer_getbyid( id, (void *)&peer ) ); | |
275 | |
276 if (!peer) { | |
277 TRACE_DEBUG(INFO, "Unable to send error '%s' to deleted peer '%s' in reply to:", error_code, id); | |
278 fd_msg_dump_walk(INFO, msg); | |
279 fd_msg_free(msg); | |
280 return 0; | |
281 } | |
282 } | |
283 } | |
284 | |
285 /* Create the error message */ | |
286 CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) ); | |
287 | |
288 /* Set the error code */ | |
289 CHECK_FCT( fd_msg_rescode_set(msg, error_code, error_message, failedavp, 1 ) ); | |
290 | |
291 /* Send the answer */ | |
292 if (is_loc) { | |
293 CHECK_FCT( fd_fifo_post(fd_g_incoming, &msg) ); | |
294 } else { | |
295 CHECK_FCT( fd_out_send(&msg, NULL, peer) ); | |
296 } | |
297 | |
298 /* Done */ | |
299 return 0; | |
300 } | |
301 | |
302 | |
303 /********************************************************************************/ | |
304 /* Second part : the threads moving messages in the daemon */ | |
305 /********************************************************************************/ | |
306 | |
307 /* Note: in the first version, we only create one thread of each kind. | |
308 We could improve the scalability by using the threshold feature of the queues | |
309 to create additional threads if a queue is filling up. | |
310 */ | |
311 | |
312 /* Control of the threads */ | |
313 enum thread_state { INITIAL = 0, RUNNING = 1, TERMINATED = 2 }; | |
314 static void cleanup_state(void * state_loc) | |
315 { | |
316 if (state_loc) | |
317 *(enum thread_state *)state_loc = TERMINATED; | |
318 } | |
319 static pthread_mutex_t order_lock = PTHREAD_MUTEX_INITIALIZER; | |
320 static enum { RUN = 0, STOP = 1 } order_val = RUN;; | |
321 | |
322 /* The dispatching thread */ | |
323 static void * dispatch_thr(void * arg) | |
324 { | |
325 TRACE_ENTRY("%p", arg); | |
326 | |
327 /* Set the thread name */ | |
328 { | |
329 char buf[48]; | |
330 snprintf(buf, sizeof(buf), "Dispatch %p", arg); | |
331 fd_log_threadname ( buf ); | |
332 } | |
333 | |
334 pthread_cleanup_push( cleanup_state, arg ); | |
335 | |
336 /* Mark the thread running */ | |
337 *(enum thread_state *)arg = RUNNING; | |
338 | |
339 do { | |
340 struct msg * msg; | |
341 struct msg_hdr * hdr; | |
342 int is_req = 0; | |
343 struct session * sess; | |
344 enum disp_action action; | |
345 const char * ec = NULL; | |
346 const char * em = NULL; | |
347 | |
348 /* Test the environment */ | |
349 { | |
350 int must_stop; | |
351 CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */ | |
352 must_stop = (order_val == STOP); | |
353 CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end ); | |
354 if (must_stop) | |
355 goto end; | |
356 | |
357 pthread_testcancel(); | |
358 } | |
359 | |
360 /* Ok, we are allowed to run */ | |
361 | |
362 /* Get the next message from the queue */ | |
363 { | |
364 int ret; | |
365 CHECK_FCT_DO( ret = fd_fifo_get ( fd_g_local, &msg ), | |
366 { | |
367 if (ret == EPIPE) | |
368 /* The queue was destroyed */ | |
369 goto end; | |
370 goto fatal_error; | |
371 } ); | |
372 } | |
373 | |
374 if (TRACE_BOOL(FULL)) { | |
375 TRACE_DEBUG(FULL, "Picked next message"); | |
376 fd_msg_dump_one(ANNOYING, msg); | |
377 } | |
378 | |
379 /* Read the message header */ | |
380 CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto fatal_error ); | |
381 is_req = hdr->msg_flags & CMD_FLAG_REQUEST; | |
382 | |
383 /* Note: if the message is for local delivery, we should test for duplicate | |
384 (draft-asveren-dime-dupcons-00). This may conflict with path validation decisions, no clear answer yet */ | |
385 | |
386 /* At this point, we need to understand the message content, so parse it */ | |
387 { | |
388 int ret; | |
389 CHECK_FCT_DO( ret = fd_msg_parse_or_error( &msg ), | |
390 { | |
391 /* in case of error, the message is already dump'd */ | |
392 if ((ret == EBADMSG) && (msg != NULL)) { | |
393 /* msg now contains the answer message to send back */ | |
394 CHECK_FCT_DO( fd_fifo_post(fd_g_outgoing, &msg), goto fatal_error ); | |
395 } | |
396 if (msg) { /* another error happen'd */ | |
397 TRACE_DEBUG(INFO, "An unexpected error occurred (%s), discarding a message:", strerror(ret)); | |
398 fd_msg_dump_walk(INFO, msg); | |
399 CHECK_FCT_DO( fd_msg_free(msg), /* continue */); | |
400 } | |
401 /* Go to the next message */ | |
402 continue; | |
403 } ); | |
404 } | |
405 | |
406 /* First, if the original request was registered with a callback and we receive the answer, call it. */ | |
407 if ( ! is_req ) { | |
408 struct msg * qry; | |
409 void (*anscb)(void *, struct msg **) = NULL; | |
410 void * data = NULL; | |
411 | |
412 /* Retrieve the corresponding query */ | |
413 CHECK_FCT_DO( fd_msg_answ_getq( msg, &qry ), goto fatal_error ); | |
414 | |
415 /* Retrieve any registered handler */ | |
416 CHECK_FCT_DO( fd_msg_anscb_get( qry, &anscb, &data ), goto fatal_error ); | |
417 | |
418 /* If a callback was registered, pass the message to it */ | |
419 if (anscb != NULL) { | |
420 | |
421 TRACE_DEBUG(FULL, "Calling callback registered when query was sent (%p, %p)", anscb, data); | |
422 (*anscb)(data, &msg); | |
423 | |
424 if (msg == NULL) { | |
425 /* Ok, the message is now handled, we can skip to the next one */ | |
426 continue; | |
427 } | |
428 } | |
429 } | |
430 | |
431 /* Retrieve the session of the message */ | |
432 CHECK_FCT_DO( fd_msg_sess_get(fd_g_config->cnf_dict, msg, &sess, NULL), goto fatal_error ); | |
433 | |
434 /* Now, call any callback registered for the message */ | |
435 CHECK_FCT_DO( fd_msg_dispatch ( &msg, sess, &action, &ec), goto fatal_error ); | |
436 | |
437 /* Now, act depending on msg and action and ec */ | |
438 if (!msg) | |
439 continue; | |
440 | |
441 switch ( action ) { | |
442 case DISP_ACT_CONT: | |
443 /* No callback has handled the message, let's reply with a generic error */ | |
444 em = "The message was not handled by any extension callback"; | |
445 ec = "DIAMETER_COMMAND_UNSUPPORTED"; | |
446 | |
447 case DISP_ACT_ERROR: | |
448 /* We have a problem with delivering the message */ | |
449 if (ec == NULL) { | |
450 ec = "DIAMETER_UNABLE_TO_COMPLY"; | |
451 } | |
452 | |
453 if (!is_req) { | |
454 TRACE_DEBUG(INFO, "Received an answer to a localy issued query, but no handler processed this answer!"); | |
455 fd_msg_dump_walk(INFO, msg); | |
456 fd_msg_free(msg); | |
457 msg = NULL; | |
458 break; | |
459 } | |
460 | |
461 /* Create an answer with the error code and message */ | |
462 CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ), goto fatal_error ); | |
463 CHECK_FCT_DO( fd_msg_rescode_set(msg, (char *)ec, (char *)em, NULL, 1 ), goto fatal_error ); | |
464 | |
465 case DISP_ACT_SEND: | |
466 /* Now, send the message */ | |
467 CHECK_FCT_DO( fd_fifo_post(fd_g_outgoing, &msg), goto fatal_error ); | |
468 } | |
469 | |
470 /* We're done with this message */ | |
471 | |
472 } while (1); | |
473 | |
474 fatal_error: | |
475 TRACE_DEBUG(INFO, "An error occurred in dispatch module! Thread is terminating..."); | |
476 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
477 | |
478 end: | |
479 /* Mark the thread as terminated */ | |
480 pthread_cleanup_pop(1); | |
481 return NULL; | |
482 } | |
483 | |
484 | |
485 /* The (routing-in) thread -- see description in freeDiameter.h */ | |
486 static void * routing_in_thr(void * arg) | |
487 { | |
488 TRACE_ENTRY("%p", arg); | |
489 | |
490 /* Set the thread name */ | |
491 { | |
492 char buf[48]; | |
493 snprintf(buf, sizeof(buf), "Routing-IN %p", arg); | |
494 fd_log_threadname ( buf ); | |
495 } | |
496 | |
497 pthread_cleanup_push( cleanup_state, arg ); | |
498 | |
499 /* Mark the thread running */ | |
500 *(enum thread_state *)arg = RUNNING; | |
501 | |
502 /* Main thread loop */ | |
503 do { | |
504 struct msg * msg; | |
505 struct msg_hdr * hdr; | |
506 int is_req = 0; | |
507 int is_err = 0; | |
508 char * qry_src = NULL; | |
509 | |
510 /* Test if we were told to stop */ | |
511 { | |
512 int must_stop; | |
513 CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */ | |
514 must_stop = (order_val == STOP); | |
515 CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end ); | |
516 if (must_stop) | |
517 goto end; | |
518 | |
519 pthread_testcancel(); | |
520 } | |
521 | |
522 /* Get the next message from the incoming queue */ | |
523 { | |
524 int ret; | |
525 CHECK_FCT_DO( ret = fd_fifo_get ( fd_g_incoming, &msg ), | |
526 { | |
527 if (ret == EPIPE) | |
528 /* The queue was destroyed */ | |
529 goto end; | |
530 goto fatal_error; | |
531 } ); | |
532 } | |
533 | |
534 if (TRACE_BOOL(FULL)) { | |
535 TRACE_DEBUG(FULL, "Picked next message"); | |
536 fd_msg_dump_one(ANNOYING, msg); | |
537 } | |
538 | |
539 /* Read the message header */ | |
540 CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto fatal_error ); | |
541 is_req = hdr->msg_flags & CMD_FLAG_REQUEST; | |
542 is_err = hdr->msg_flags & CMD_FLAG_ERROR; | |
543 | |
544 /* Handle incorrect bits */ | |
545 if (is_req && is_err) { | |
546 CHECK_FCT_DO( return_error( msg, "DIAMETER_INVALID_HDR_BITS", "R & E bits were set", NULL), goto fatal_error ); | |
547 continue; | |
548 } | |
549 | |
550 /* If it is a request, we must analyze its content to decide what we do with it */ | |
551 if (is_req) { | |
552 struct avp * avp, *un = NULL; | |
553 union avp_value * un_val = NULL, *dr_val = NULL; | |
554 enum status { UNKNOWN, YES, NO }; | |
555 /* Are we Destination-Host? */ | |
556 enum status is_dest_host = UNKNOWN; | |
557 /* Are we Destination-Realm? */ | |
558 enum status is_dest_realm = UNKNOWN; | |
559 /* Do we support the application of the message? */ | |
560 enum status is_local_app = UNKNOWN; | |
561 | |
562 /* Check if we have local support for the message application */ | |
563 if ( (hdr->msg_appl == 0) || (hdr->msg_appl == AI_RELAY) ) { | |
564 TRACE_DEBUG(INFO, "Received a routable message with application id 0, returning DIAMETER_APPLICATION_UNSUPPORTED"); | |
565 CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", "Routable message with application id 0 or relay", NULL), goto fatal_error ); | |
566 continue; | |
567 } else { | |
568 struct fd_app * app; | |
569 CHECK_FCT_DO( fd_app_check(&fd_g_config->cnf_apps, hdr->msg_appl, &app), goto fatal_error ); | |
570 is_local_app = (app ? YES : NO); | |
571 } | |
572 | |
573 /* Parse the message for Dest-Host and Dest-Realm */ | |
574 CHECK_FCT_DO( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL), goto fatal_error ); | |
575 while (avp) { | |
576 struct avp_hdr * ahdr; | |
577 CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), goto fatal_error ); | |
578 | |
579 if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { | |
580 switch (ahdr->avp_code) { | |
581 case AC_DESTINATION_HOST: | |
582 /* Parse this AVP */ | |
583 CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ), goto fatal_error ); | |
584 ASSERT( ahdr->avp_value ); | |
585 /* Compare the Destination-Host AVP of the message with our identity */ | |
586 if (ahdr->avp_value->os.len != fd_g_config->cnf_diamid_len) { | |
587 is_dest_host = NO; | |
588 } else { | |
589 is_dest_host = (strncasecmp(fd_g_config->cnf_diamid, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamid_len) | |
590 ? NO : YES); | |
591 } | |
592 break; | |
593 | |
594 case AC_DESTINATION_REALM: | |
595 /* Parse this AVP */ | |
596 CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ), goto fatal_error ); | |
597 ASSERT( ahdr->avp_value ); | |
598 dr_val = ahdr->avp_value; | |
599 /* Compare the Destination-Realm AVP of the message with our identity */ | |
600 if (ahdr->avp_value->os.len != fd_g_config->cnf_diamrlm_len) { | |
601 is_dest_realm = NO; | |
602 } else { | |
603 is_dest_realm = (strncasecmp(fd_g_config->cnf_diamrlm, (char *)ahdr->avp_value->os.data, fd_g_config->cnf_diamrlm_len) | |
604 ? NO : YES); | |
605 } | |
606 break; | |
607 | |
608 case AC_USER_NAME: | |
609 /* Parse this AVP */ | |
610 CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ), goto fatal_error ); | |
611 ASSERT( ahdr->avp_value ); | |
612 un = avp; | |
613 un_val = ahdr->avp_value; | |
614 break; | |
615 } | |
616 } | |
617 | |
618 if ((is_dest_host != UNKNOWN) && (is_dest_realm != UNKNOWN) && un) | |
619 break; | |
620 | |
621 /* Go to next AVP */ | |
622 CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), goto fatal_error ); | |
623 } | |
624 | |
625 /* OK, now decide what we do with the request */ | |
626 | |
627 /* Handle the missing routing AVPs first */ | |
628 if ( is_dest_realm == UNKNOWN ) { | |
629 CHECK_FCT_DO( return_error( msg, "DIAMETER_COMMAND_UNSUPPORTED", "Non-routable message not supported (invalid bit ? missing Destination-Realm ?)", NULL), goto fatal_error ); | |
630 continue; | |
631 } | |
632 | |
633 /* If we are listed as Destination-Host */ | |
634 if (is_dest_host == YES) { | |
635 if (is_local_app == YES) { | |
636 /* Ok, give the message to the dispatch thread */ | |
637 CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); | |
638 } else { | |
639 /* We don't support the application, reply an error */ | |
640 CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL), goto fatal_error ); | |
641 } | |
642 continue; | |
643 } | |
644 | |
645 /* If the message is explicitely for someone else */ | |
646 if ((is_dest_host == NO) || (is_dest_realm == NO)) { | |
647 if (fd_g_config->cnf_flags.no_fwd) { | |
648 CHECK_FCT_DO( return_error( msg, "DIAMETER_UNABLE_TO_DELIVER", "This peer is not an agent", NULL), goto fatal_error ); | |
649 continue; | |
650 } | |
651 } else { | |
652 /* Destination-Host was not set, and Destination-Realm is matching : we may handle or pass to a fellow peer */ | |
653 | |
654 /* test for decorated NAI (draft-ietf-dime-nai-routing-04 section 4.4) */ | |
655 if (is_decorated_NAI(un_val)) { | |
656 /* Handle the decorated NAI */ | |
657 CHECK_FCT_DO( process_decorated_NAI(un_val, dr_val), | |
658 { | |
659 /* If the process failed, we assume it is because of the AVP format */ | |
660 CHECK_FCT_DO( return_error( msg, "DIAMETER_INVALID_AVP_VALUE", "Failed to process decorated NAI", un), goto fatal_error ); | |
661 continue; | |
662 } ); | |
663 | |
664 /* We have transformed the AVP, now submit it again in the queue */ | |
665 CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto fatal_error ); | |
666 continue; | |
667 } | |
668 | |
669 if (is_local_app == YES) { | |
670 /* Handle localy since we are able to */ | |
671 CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); | |
672 continue; | |
673 } | |
674 | |
675 if (fd_g_config->cnf_flags.no_fwd) { | |
676 /* We return an error */ | |
677 CHECK_FCT_DO( return_error( msg, "DIAMETER_APPLICATION_UNSUPPORTED", NULL, NULL), goto fatal_error ); | |
678 continue; | |
679 } | |
680 } | |
681 | |
682 /* From that point, for requests, we will call the registered callbacks, then forward to another peer */ | |
683 | |
684 } else { | |
685 /* The message is an answer */ | |
686 struct msg * qry; | |
687 | |
688 /* Retrieve the corresponding query and its origin */ | |
689 CHECK_FCT_DO( fd_msg_answ_getq( msg, &qry ), goto fatal_error ); | |
690 CHECK_FCT_DO( fd_msg_source_get( qry, &qry_src ), goto fatal_error ); | |
691 | |
692 if ((!qry_src) && (!is_err)) { | |
693 /* The message is a normal answer to a request issued localy, we do not call the callbacks chain on it. */ | |
694 CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); | |
695 continue; | |
696 } | |
697 | |
698 /* From that point, for answers, we will call the registered callbacks, then pass it to the dispatch module or forward it */ | |
699 } | |
700 | |
701 /* Call all registered callbacks for this message */ | |
702 { | |
703 struct fd_list * li; | |
704 | |
705 CHECK_FCT_DO( pthread_rwlock_rdlock( &rt_fwd_lock ), goto fatal_error ); | |
706 pthread_cleanup_push( fd_cleanup_rwlock, &rt_fwd_lock ); | |
707 | |
708 /* requests: dir = 1 & 2 => in order; answers = 3 & 2 => in reverse order */ | |
709 for ( li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; msg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) { | |
710 struct rt_hdl * rh = (struct rt_hdl *)li; | |
711 | |
712 if (is_req && (rh->dir > RT_FWD_ALL)) | |
713 break; | |
714 if ((!is_req) && (rh->dir < RT_FWD_ALL)) | |
715 break; | |
716 | |
717 /* Ok, call this cb */ | |
718 TRACE_DEBUG(ANNOYING, "Calling next FWD callback on %p : %p", msg, rh->rt_fwd_cb); | |
719 CHECK_FCT_DO( (*rh->rt_fwd_cb)(rh->cbdata, &msg), | |
720 { | |
721 TRACE_DEBUG(INFO, "A FWD routing callback returned an error, message discarded."); | |
722 fd_msg_dump_walk(INFO, msg); | |
723 fd_msg_free(msg); | |
724 msg = NULL; | |
725 } ); | |
726 } | |
727 | |
728 pthread_cleanup_pop(0); | |
729 CHECK_FCT_DO( pthread_rwlock_unlock( &rt_fwd_lock ), goto fatal_error ); | |
730 | |
731 /* If a callback has handled the message, we stop now */ | |
732 if (!msg) | |
733 continue; | |
734 } | |
735 | |
736 /* Now handle the message to the next step: either forward to another peer, or for local delivery */ | |
737 if (is_req || qry_src) { | |
738 CHECK_FCT_DO(fd_fifo_post(fd_g_outgoing, &msg), goto fatal_error ); | |
739 } else { | |
740 CHECK_FCT_DO(fd_fifo_post(fd_g_local, &msg), goto fatal_error ); | |
741 } | |
742 | |
743 /* We're done with this message */ | |
744 } while (1); | |
745 | |
746 fatal_error: | |
747 TRACE_DEBUG(INFO, "An error occurred in routing module! IN thread is terminating..."); | |
748 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
749 | |
750 end: | |
751 /* Mark the thread as terminated */ | |
752 pthread_cleanup_pop(1); | |
753 return NULL; | |
754 } | |
755 | |
756 | |
757 /* The (routing-out) thread -- see description in freeDiameter.h */ | |
758 static void * routing_out_thr(void * arg) | |
759 { | |
760 struct rt_data * rtd = NULL; | |
761 TRACE_ENTRY("%p", arg); | |
762 | |
763 /* Set the thread name */ | |
764 { | |
765 char buf[48]; | |
766 snprintf(buf, sizeof(buf), "Routing OUT %p", arg); | |
767 fd_log_threadname ( buf ); | |
768 } | |
769 | |
770 pthread_cleanup_push( cleanup_state, arg ); | |
771 | |
772 /* Mark the thread running */ | |
773 *(enum thread_state *)arg = RUNNING; | |
774 | |
775 | |
776 /* Main thread loop */ | |
777 do { | |
778 struct msg * msg; | |
779 struct msg_hdr * hdr; | |
780 int is_req = 0; | |
781 struct fd_list * li, *candidates; | |
782 struct avp * avp; | |
783 struct rtd_candidate * c; | |
784 | |
785 /* If we loop'd with some undeleted routing data, destroy it */ | |
786 if (rtd != NULL) | |
787 fd_rtd_free(&rtd); | |
788 | |
789 /* Test if we were told to stop */ | |
790 { | |
791 int must_stop; | |
792 CHECK_POSIX_DO( pthread_mutex_lock(&order_lock), goto end ); /* we lock to flush the caches */ | |
793 must_stop = (order_val == STOP); | |
794 CHECK_POSIX_DO( pthread_mutex_unlock(&order_lock), goto end ); | |
795 if (must_stop) | |
796 goto end; | |
797 | |
798 pthread_testcancel(); | |
799 } | |
800 | |
801 /* Get the next message from the ougoing queue */ | |
802 { | |
803 int ret; | |
804 CHECK_FCT_DO( ret = fd_fifo_get ( fd_g_outgoing, &msg ), | |
805 { | |
806 if (ret == EPIPE) | |
807 /* The queue was destroyed */ | |
808 goto end; | |
809 goto fatal_error; | |
810 } ); | |
811 } | |
812 | |
813 if (TRACE_BOOL(FULL)) { | |
814 TRACE_DEBUG(FULL, "Picked next message"); | |
815 fd_msg_dump_one(ANNOYING, msg); | |
816 } | |
817 | |
818 /* Read the message header */ | |
819 CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto fatal_error ); | |
820 is_req = hdr->msg_flags & CMD_FLAG_REQUEST; | |
821 | |
822 /* For answers, the routing is very easy */ | |
823 if ( ! is_req ) { | |
824 struct msg * qry; | |
825 char * qry_src = NULL; | |
826 struct msg_hdr * qry_hdr; | |
827 struct fd_peer * peer = NULL; | |
828 | |
829 /* Retrieve the corresponding query and its origin */ | |
830 CHECK_FCT_DO( fd_msg_answ_getq( msg, &qry ), goto fatal_error ); | |
831 CHECK_FCT_DO( fd_msg_source_get( qry, &qry_src ), goto fatal_error ); | |
832 | |
833 ASSERT( qry_src ); /* if it is NULL, the message should have been in the LOCAL queue! */ | |
834 | |
835 /* Find the peer corresponding to this name */ | |
836 CHECK_FCT_DO( fd_peer_getbyid( qry_src, (void *) &peer ), goto fatal_error ); | |
837 if ((!peer) || (peer->p_hdr.info.runtime.pir_state != STATE_OPEN)) { | |
838 TRACE_DEBUG(INFO, "Unable to forward answer message to peer '%s', deleted or not in OPEN state.", qry_src); | |
839 fd_msg_dump_walk(INFO, msg); | |
840 fd_msg_free(msg); | |
841 continue; | |
842 } | |
843 | |
844 /* We must restore the hop-by-hop id */ | |
845 CHECK_FCT_DO( fd_msg_hdr(qry, &qry_hdr), goto fatal_error ); | |
846 hdr->msg_hbhid = qry_hdr->msg_hbhid; | |
847 | |
848 /* Push the message into this peer */ | |
849 CHECK_FCT_DO( fd_out_send(&msg, NULL, peer), goto fatal_error ); | |
850 | |
851 /* We're done with this answer */ | |
852 continue; | |
853 } | |
854 | |
855 /* From that point, the message is a request */ | |
856 | |
857 /* Get the routing data out of the message if any (in case of re-transmit) */ | |
858 CHECK_FCT_DO( fd_msg_rt_get ( msg, &rtd ), goto fatal_error ); | |
859 | |
860 /* If there is no routing data already, let's create it */ | |
861 if (rtd == NULL) { | |
862 CHECK_FCT_DO( fd_rtd_init(&rtd), goto fatal_error ); | |
863 | |
864 /* Add all peers in OPEN state */ | |
865 CHECK_FCT_DO( pthread_rwlock_wrlock(&fd_g_activ_peers_rw), goto fatal_error ); | |
866 for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { | |
867 struct fd_peer * p = (struct fd_peer *)li->o; | |
868 CHECK_FCT_DO( fd_rtd_candidate_add(rtd, p->p_hdr.info.pi_diamid), goto fatal_error); | |
869 } | |
870 CHECK_FCT_DO( pthread_rwlock_unlock(&fd_g_activ_peers_rw), goto fatal_error ); | |
871 | |
872 /* Now let's remove all peers from the Route-Records */ | |
873 CHECK_FCT_DO( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL), goto fatal_error ); | |
874 while (avp) { | |
875 struct avp_hdr * ahdr; | |
876 CHECK_FCT_DO( fd_msg_avp_hdr( avp, &ahdr ), goto fatal_error ); | |
877 | |
878 if ((ahdr->avp_code == AC_ROUTE_RECORD) && (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) ) { | |
879 /* Parse this AVP */ | |
880 CHECK_FCT_DO( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ), goto fatal_error ); | |
881 ASSERT( ahdr->avp_value ); | |
882 /* Remove this value from the list */ | |
883 fd_rtd_candidate_del(rtd, (char *)ahdr->avp_value->os.data, ahdr->avp_value->os.len); | |
884 } | |
885 | |
886 /* Go to next AVP */ | |
887 CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), goto fatal_error ); | |
888 } | |
889 } | |
890 | |
891 /* Note: we reset the scores and pass the message to the callbacks, maybe we could re-use the saved scores when we have received an error ? */ | |
892 | |
893 /* Ok, we have our list in rtd now, let's (re)initialize the scores */ | |
894 fd_rtd_candidate_extract(rtd, &candidates); | |
895 | |
896 /* Pass the list to registered callbacks (even if it is empty) */ | |
897 { | |
898 CHECK_FCT_DO( pthread_rwlock_rdlock( &rt_out_lock ), goto fatal_error ); | |
899 pthread_cleanup_push( fd_cleanup_rwlock, &rt_out_lock ); | |
900 | |
901 /* We call the cb by reverse priority order */ | |
902 for ( li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) { | |
903 struct rt_hdl * rh = (struct rt_hdl *)li; | |
904 | |
905 TRACE_DEBUG(ANNOYING, "Calling next OUT callback on %p : %p (prio %d)", msg, rh->rt_out_cb, rh->prio); | |
906 CHECK_FCT_DO( (*rh->rt_out_cb)(rh->cbdata, msg, candidates), | |
907 { | |
908 TRACE_DEBUG(INFO, "An OUT routing callback returned an error ! Message discarded."); | |
909 fd_msg_dump_walk(INFO, msg); | |
910 fd_msg_free(msg); | |
911 msg = NULL; | |
912 break; | |
913 } ); | |
914 } | |
915 | |
916 pthread_cleanup_pop(0); | |
917 CHECK_FCT_DO( pthread_rwlock_unlock( &rt_out_lock ), goto fatal_error ); | |
918 | |
919 /* If an error occurred, skip to the next message */ | |
920 if (!msg) | |
921 continue; | |
922 } | |
923 | |
924 /* Order the candidate peers by score attributed by the callbacks */ | |
925 CHECK_FCT_DO( fd_rtd_candidate_reorder(candidates), goto fatal_error ); | |
926 | |
927 /* Save the routing information in the message */ | |
928 CHECK_FCT_DO( fd_msg_rt_associate ( msg, &rtd ), goto fatal_error ); | |
929 | |
930 /* Now try sending the message */ | |
931 for (li = candidates->prev; li != candidates; li = li->prev) { | |
932 struct fd_peer * peer; | |
933 | |
934 c = (struct rtd_candidate *) li; | |
935 | |
936 /* Stop when we have reached the end of valid candidates */ | |
937 if (c->score < 0) | |
938 break; | |
939 | |
940 /* Search for the peer */ | |
941 CHECK_FCT_DO( fd_peer_getbyid( c->diamid, (void *)&peer ), goto fatal_error ); | |
942 | |
943 if (peer && (peer->p_hdr.info.runtime.pir_state == STATE_OPEN)) { | |
944 /* Send to this one */ | |
945 CHECK_FCT_DO( fd_out_send(&msg, NULL, peer), continue ); | |
946 /* If the sending was successful */ | |
947 break; | |
948 } | |
949 } | |
950 | |
951 /* If the message has not been sent, return an error */ | |
952 if (msg) { | |
953 TRACE_DEBUG(INFO, "Could not send the following message, replying with UNABLE_TO_DELIVER"); | |
954 fd_msg_dump_walk(INFO, msg); | |
955 return_error( msg, "DIAMETER_UNABLE_TO_DELIVER", "No suitable candidate to route the message to", NULL); | |
956 } | |
957 | |
958 /* We're done with this message */ | |
959 | |
960 } while (1); | |
961 | |
962 fatal_error: | |
963 TRACE_DEBUG(INFO, "An error occurred in routing module! OUT thread is terminating..."); | |
964 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); | |
965 | |
966 end: | |
967 /* Mark the thread as terminated */ | |
968 pthread_cleanup_pop(1); | |
969 return NULL; | |
970 } | |
971 | |
972 | |
973 /********************************************************************************/ | |
974 /* Some default routing callbacks */ | |
975 /********************************************************************************/ | |
976 | |
977 /* First OUT callback: prevent sending to peers that do not support the message application */ | |
978 static int dont_send_if_no_common_app(void * cbdata, struct msg * msg, struct fd_list * candidates) | |
979 { | |
980 struct fd_list * li; | |
981 struct msg_hdr * hdr; | |
982 | |
983 TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); | |
984 CHECK_PARAMS(msg && candidates); | |
985 | |
986 CHECK_FCT( fd_msg_hdr(msg, &hdr) ); | |
987 | |
988 /* For Base Diameter Protocol, every peer is supposed to support it, so skip */ | |
989 if (hdr->msg_appl == 0) | |
990 return 0; | |
991 | |
992 /* Otherwise, check that the peers support the application */ | |
993 for (li = candidates->next; li != candidates; li = li->next) { | |
994 struct rtd_candidate *c = (struct rtd_candidate *) li; | |
995 struct fd_peer * peer; | |
996 struct fd_app *found; | |
997 CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) ); | |
998 if (peer && (peer->p_hdr.info.runtime.pir_relay == 0)) { | |
999 /* Check if the remote peer advertised the message's appli */ | |
1000 CHECK_FCT( fd_app_check(&peer->p_hdr.info.runtime.pir_apps, hdr->msg_appl, &found) ); | |
1001 if (!found) | |
1002 c->score += FD_SCORE_NO_DELIVERY; | |
1003 } | |
1004 } | |
1005 | |
1006 return 0; | |
1007 } | |
1008 | |
1009 /* Second OUT callback: Detect if the Destination-Host and Destination-Realm match the peer */ | |
1010 static int score_destination_avp(void * cbdata, struct msg * msg, struct fd_list * candidates) | |
1011 { | |
1012 struct fd_list * li; | |
1013 struct avp * avp; | |
1014 union avp_value *dh = NULL, *dr = NULL; | |
1015 | |
1016 TRACE_ENTRY("%p %p %p", cbdata, msg, candidates); | |
1017 CHECK_PARAMS(msg && candidates); | |
1018 | |
1019 /* Search the Destination-Host and Destination-Realm AVPs -- we could also use fd_msg_search_avp here, but this one is slightly more efficient */ | |
1020 CHECK_FCT( fd_msg_browse(msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); | |
1021 while (avp) { | |
1022 struct avp_hdr * ahdr; | |
1023 CHECK_FCT( fd_msg_avp_hdr( avp, &ahdr ) ); | |
1024 | |
1025 if (! (ahdr->avp_flags & AVP_FLAG_VENDOR)) { | |
1026 switch (ahdr->avp_code) { | |
1027 case AC_DESTINATION_HOST: | |
1028 /* Parse this AVP */ | |
1029 CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); | |
1030 ASSERT( ahdr->avp_value ); | |
1031 dh = ahdr->avp_value; | |
1032 break; | |
1033 | |
1034 case AC_DESTINATION_REALM: | |
1035 /* Parse this AVP */ | |
1036 CHECK_FCT( fd_msg_parse_dict ( avp, fd_g_config->cnf_dict, NULL ) ); | |
1037 ASSERT( ahdr->avp_value ); | |
1038 dr = ahdr->avp_value; | |
1039 break; | |
1040 } | |
1041 } | |
1042 | |
1043 if (dh && dr) | |
1044 break; | |
1045 | |
1046 /* Go to next AVP */ | |
1047 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); | |
1048 } | |
1049 | |
1050 /* Now, check each candidate against these AVP values */ | |
1051 for (li = candidates->next; li != candidates; li = li->next) { | |
1052 struct rtd_candidate *c = (struct rtd_candidate *) li; | |
1053 struct fd_peer * peer; | |
1054 CHECK_FCT( fd_peer_getbyid( c->diamid, (void *)&peer ) ); | |
1055 if (peer) { | |
1056 if (dh | |
1057 && (dh->os.len == strlen(peer->p_hdr.info.pi_diamid)) | |
1058 && (strncasecmp(peer->p_hdr.info.pi_diamid, dh->os.data, dh->os.len) == 0)) { | |
1059 /* The candidate is the Destination-Host */ | |
1060 c->score += FD_SCORE_FINALDEST; | |
1061 } else { | |
1062 if (dr && peer->p_hdr.info.runtime.pir_realm | |
1063 && (dr->os.len == strlen(peer->p_hdr.info.runtime.pir_realm)) | |
1064 && (strncasecmp(peer->p_hdr.info.runtime.pir_realm, dr->os.data, dr->os.len) == 0)) { | |
1065 /* The candidate's realm matchs the Destination-Realm */ | |
1066 c->score += FD_SCORE_REALM; | |
1067 } | |
1068 } | |
1069 } | |
1070 } | |
1071 | |
1072 return 0; | |
1073 } | |
1074 | |
1075 /********************************************************************************/ | |
1076 /* The functions for the other files */ | |
1077 /********************************************************************************/ | |
1078 | |
1079 /* Later: TODO("Set thresholds on queues"); */ | |
1080 static pthread_t dispatch = (pthread_t)NULL; | |
1081 static enum thread_state disp_state = INITIAL; | |
1082 | |
1083 static pthread_t rt_out = (pthread_t)NULL; | |
1084 static enum thread_state out_state = INITIAL; | |
1085 | |
1086 static pthread_t rt_in = (pthread_t)NULL; | |
1087 static enum thread_state in_state = INITIAL; | |
1088 | |
1089 /* Initialize the routing and dispatch threads */ | |
1090 int fd_rtdisp_init(void) | |
1091 { | |
1092 CHECK_POSIX( pthread_create( &dispatch, NULL, dispatch_thr, &disp_state ) ); | |
1093 CHECK_POSIX( pthread_create( &rt_out, NULL, routing_out_thr, &out_state) ); | |
1094 CHECK_POSIX( pthread_create( &rt_in, NULL, routing_in_thr, &in_state) ); | |
1095 | |
1096 /* Later: TODO("Set the thresholds for the queues to create more threads as needed"); */ | |
1097 | |
1098 /* Register the built-in callbacks */ | |
1099 CHECK_FCT( fd_rt_out_register( dont_send_if_no_common_app, NULL, 10, NULL ) ); | |
1100 CHECK_FCT( fd_rt_out_register( score_destination_avp, NULL, 10, NULL ) ); | |
1101 return 0; | |
1102 } | |
1103 | |
1104 /* Ask the thread to terminate after next iteration */ | |
1105 int fd_rtdisp_cleanstop(void) | |
1106 { | |
1107 CHECK_POSIX( pthread_mutex_lock(&order_lock) ); | |
1108 order_val = STOP; | |
1109 CHECK_POSIX( pthread_mutex_unlock(&order_lock) ); | |
1110 | |
1111 return 0; | |
1112 } | |
1113 | |
1114 /* Stop the thread after up to one second of wait */ | |
1115 int fd_rtdisp_fini(void) | |
1116 { | |
1117 /* Destroy the local queue */ | |
1118 CHECK_FCT_DO( fd_queues_fini_disp(), /* ignore */); | |
1119 | |
1120 /* Wait for a second for the thread to complete, by monitoring my_state */ | |
1121 if (disp_state != TERMINATED) { | |
1122 TRACE_DEBUG(INFO, "Waiting for the dispatch thread to have a chance to terminate"); | |
1123 do { | |
1124 struct timespec ts, ts_final; | |
1125 | |
1126 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break ); | |
1127 | |
1128 ts_final.tv_sec = ts.tv_sec + 1; | |
1129 ts_final.tv_nsec = ts.tv_nsec; | |
1130 | |
1131 while (TS_IS_INFERIOR( &ts, &ts_final )) { | |
1132 if (disp_state == TERMINATED) | |
1133 break; | |
1134 | |
1135 usleep(100000); | |
1136 CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), break ); | |
1137 } | |
1138 } while (0); | |
1139 } | |
1140 | |
1141 /* Now stop the thread and reclaim its resources */ | |
1142 CHECK_FCT_DO( fd_thr_term(&dispatch ), /* continue */); | |
1143 | |
1144 | |
1145 TODO("Add terminating the routing threads"); | |
1146 return 0; | |
1147 } | |
1148 | |
1149 /* Cleanup handlers */ | |
1150 int fd_rtdisp_cleanup(void) | |
1151 { | |
1152 /* Cleanup all remaining handlers */ | |
1153 while (!FD_IS_LIST_EMPTY(&rt_fwd_list)) { | |
1154 CHECK_FCT_DO( fd_rt_fwd_unregister ( (void *)rt_fwd_list.next, NULL ), /* continue */ ); | |
1155 } | |
1156 while (!FD_IS_LIST_EMPTY(&rt_out_list)) { | |
1157 CHECK_FCT_DO( fd_rt_out_unregister ( (void *)rt_out_list.next, NULL ), /* continue */ ); | |
1158 } | |
1159 | |
1160 fd_disp_unregister_all(); /* destroy remaining handlers */ | |
1161 | |
1162 return 0; | |
1163 } | |
1164 | |
1165 | |
1166 /* Add an application into the peer's supported apps */ | |
1167 int fd_disp_app_support ( struct dict_object * app, struct dict_object * vendor, int auth, int acct ) | |
1168 { | |
1169 application_id_t aid = 0; | |
1170 vendor_id_t vid = 0; | |
1171 | |
1172 TRACE_ENTRY("%p %p %d %d", app, vendor, auth, acct); | |
1173 CHECK_PARAMS( app && (auth || acct) ); | |
1174 | |
1175 { | |
1176 enum dict_object_type type = 0; | |
1177 struct dict_application_data data; | |
1178 CHECK_FCT( fd_dict_gettype(app, &type) ); | |
1179 CHECK_PARAMS( type == DICT_APPLICATION ); | |
1180 CHECK_FCT( fd_dict_getval(app, &data) ); | |
1181 aid = data.application_id; | |
1182 } | |
1183 | |
1184 if (vendor) { | |
1185 enum dict_object_type type = 0; | |
1186 struct dict_vendor_data data; | |
1187 CHECK_FCT( fd_dict_gettype(vendor, &type) ); | |
1188 CHECK_PARAMS( type == DICT_VENDOR ); | |
1189 CHECK_FCT( fd_dict_getval(vendor, &data) ); | |
1190 vid = data.vendor_id; | |
1191 } | |
1192 | |
1193 return fd_app_merge(&fd_g_config->cnf_apps, aid, vid, auth, acct); | |
1194 } | |
1195 | |
1196 | |
1197 |