Navigation


source: freeDiameter/libfdcore/p_ce.c @ 706:4ffbc9f1e922

Last change on this file since 706:4ffbc9f1e922 was 706:4ffbc9f1e922, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 11 years ago

Large UNTESTED commit with the following changes:

  • Improved DiameterIdentity? handling (esp. interationalization issues), and improve efficiency of some string operations in peers, sessions, and dictionary modules (closes #7)
  • Cleanup in the session module to free only unreferenced sessions (#16)
  • Removed fd_cpu_flush_cache(), replaced by more robust alternatives.
  • Improved peer state machine algorithm to counter SCTP multistream race condition.
File size: 36.3 KB
Line 
1/*********************************************************************************************************
2* Software License Agreement (BSD License)                                                               *
3* Author: Sebastien Decugis <sdecugis@nict.go.jp>                                                        *
4*                                                                                                        *
5* Copyright (c) 2011, 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 "fdcore-internal.h"
37
38/* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */
39
40/* Save a connection as peer's principal */
41static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx)
42{
43        CHECK_PARAMS( peer->p_cnxctx == NULL );
44       
45        /* Save the connection in peer */
46        peer->p_cnxctx = *cnx;
47        *cnx = NULL;
48       
49        /* Set the events to be sent to the PSM */
50        CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) );
51       
52        /* Read the credentials if possible */
53        if (fd_cnx_getTLS(peer->p_cnxctx)) {
54                CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
55        }
56       
57        /* Read the endpoints, maybe used to reconnect to the peer later */
58        CHECK_FCT( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints) );
59       
60        /* Read the protocol */
61        peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx);
62       
63        return 0;
64}
65
66/* Delete the peer connection, and cleanup associated information */
67void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept)
68{
69        peer->p_hdr.info.runtime.pir_cert_list = NULL;
70        peer->p_hdr.info.runtime.pir_cert_list_size = 0;
71        peer->p_hdr.info.runtime.pir_proto = 0;
72       
73        if (peer->p_cnxctx) {
74                if (cnx_kept != NULL) {
75                        *cnx_kept = peer->p_cnxctx;
76                } else {
77                        fd_cnx_destroy(peer->p_cnxctx);
78                }
79                peer->p_cnxctx = NULL;
80        }
81}
82
83/* Election: compare the Diameter Ids by lexical order, return true if the election is won */
84static __inline__ int election_result(struct fd_peer * peer)
85{
86        int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0);
87        if (ret) {
88                TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid);
89        } else {
90                TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid);
91        }
92        return ret;
93}
94
95/* Add AVPs about local information in a CER or CEA */
96static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none)
97{
98        struct dict_object * dictobj = NULL;
99        struct avp * avp = NULL;
100        union avp_value val;
101        struct fd_list *li;
102       
103        /* Add the Origin-* AVPs */
104        CHECK_FCT( fd_msg_add_origin ( msg, 1 ) );
105       
106        /* Find the model for Host-IP-Address AVP */
107        CHECK_FCT(  fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT )  );
108               
109        /* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */
110        for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) {
111                struct fd_endpoint * ep = (struct fd_endpoint *)li;
112               
113                CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
114                CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) );
115                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
116        }
117       
118        /* Vendor-Id, Product-Name, and Firmware-Revision AVPs */
119        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT )  );
120        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
121        val.u32 = MY_VENDOR_ID;
122        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
123        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
124       
125        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT )  );
126        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
127        val.os.data = (unsigned char *)FD_PROJECT_NAME;
128        val.os.len = strlen(FD_PROJECT_NAME);
129        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
130        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
131       
132        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT )  );
133        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
134        val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV);
135        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
136        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
137       
138       
139        /* Add the Inband-Security-Id AVP if needed */
140        if (isi_tls || isi_none) {
141                CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT )  );
142               
143                if (isi_none) {
144                        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
145                        val.u32 = ACV_ISI_NO_INBAND_SECURITY;
146                        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
147                        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
148                }
149               
150                if (isi_tls) {
151                        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
152                        val.u32 = ACV_ISI_TLS;
153                        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
154                        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
155                }
156        }
157       
158        /* List of local applications */
159        {
160                struct dict_object * dictobj_auth = NULL;
161                struct dict_object * dictobj_acct = NULL;
162                struct dict_object * dictobj_vid = NULL;
163               
164                CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT )  );
165                CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT )  );
166                CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT )  );
167                CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT )  );
168               
169                for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) {
170                        struct fd_app * a = (struct fd_app *)(li);
171
172                        if (a->flags.auth) {
173                                CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
174                                val.u32 = a->appid;
175                                CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
176                                if (a->vndid != 0) {
177                                        struct avp * avp2 = NULL;
178                                        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
179                                        CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
180                                        avp = avp2;
181                                        CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
182                                        val.u32 = a->vndid;
183                                        CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
184                                        CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
185                                }
186                                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
187                        }
188                        if (a->flags.acct) {
189                                CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) );
190                                val.u32 = a->appid;
191                                CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
192                                if (a->vndid != 0) {
193                                        struct avp * avp2 = NULL;
194                                        CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) );
195                                        CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) );
196                                        avp = avp2;
197                                        CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) );
198                                        val.u32 = a->vndid;
199                                        CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) );
200                                        CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) );
201                                }
202                                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
203                        }
204                }
205               
206                /* do not forget the relay application */
207                if (! fd_g_config->cnf_flags.no_fwd) {
208                        CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) );
209                        val.u32 = AI_RELAY;
210                        CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
211                        CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
212                }
213        }
214       
215        /* Add the list of supported vendors */
216        {
217                uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict);
218                if (array) {
219                        int i = 0;
220                        CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT )  );
221                       
222                        while (array[i] != 0) {
223                                CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) );
224                                val.u32 = array[i];
225                                CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) );
226                                CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) );
227                                i++;
228                        }
229                       
230                        free(array);
231                }
232        }
233       
234        return 0;
235}
236
237/* Remove any information saved from a previous CER/CEA exchange */
238static void cleanup_remote_CE_info(struct fd_peer * peer)
239{
240        /* free linked information */
241        free(peer->p_hdr.info.runtime.pir_realm);
242        free(peer->p_hdr.info.runtime.pir_prodname);
243        while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) {
244                struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next;
245                fd_list_unlink(li);
246                free(li);
247        }
248        /* note: pir_cert_list needs not be freed (belongs to gnutls) */
249       
250        /* cleanup the area */
251        memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime));
252       
253        /* reinit the list */
254        fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer);
255
256        /* Remove previously advertised endpoints */
257        fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV );
258}
259
260/* Extract information sent by the remote peer and save it in our peer structure */
261static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc)
262{
263        struct avp * avp = NULL;
264       
265        cleanup_remote_CE_info(peer);
266       
267        CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) );
268       
269        /* Loop on all AVPs and save what we are interrested into */
270        while (avp) {
271                struct avp_hdr * hdr;
272
273                CHECK_FCT(  fd_msg_avp_hdr( avp, &hdr )  );
274
275                if (hdr->avp_flags & AVP_FLAG_VENDOR) {
276                        /* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */
277                        TRACE_DEBUG(FULL, "Ignored a vendor AVP in CER / CEA");
278                        fd_msg_dump_one(FULL, avp);
279                        goto next;
280                }
281
282                switch (hdr->avp_code) {
283                        case AC_RESULT_CODE: /* Result-Code */
284                                if (hdr->avp_value == NULL) {
285                                        /* This is a sanity check */
286                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
287                                        fd_msg_dump_one(NONE, avp);
288                                        ASSERT(0); /* To check if this really happens, and understand why... */
289                                        goto next;
290                                }
291                               
292                                if (rc)
293                                        *rc = hdr->avp_value->u32;
294                                break;
295               
296                        case AC_ORIGIN_HOST: /* Origin-Host */
297                                if (hdr->avp_value == NULL) {
298                                        /* This is a sanity check */
299                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
300                                        fd_msg_dump_one(NONE, avp);
301                                        ASSERT(0); /* To check if this really happens, and understand why... */
302                                        goto next;
303                                }
304                               
305                                /* We check that the value matches what we know, otherwise disconnect the peer */
306                                if (fd_os_almostcasecmp(hdr->avp_value->os.data, hdr->avp_value->os.len, 
307                                                        peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen)) {
308                                        TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", 
309                                                        hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid);
310                                        error->pei_errcode = "ER_DIAMETER_AVP_NOT_ALLOWED";
311                                        error->pei_message = "Your Origin-Host value does not match my configuration.";
312                                        error->pei_avp = avp;
313                                        return EINVAL;
314                                }
315
316                                break;
317               
318                        case AC_ORIGIN_REALM: /* Origin-Realm */
319                                if (hdr->avp_value == NULL) {
320                                        /* This is a sanity check */
321                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
322                                        fd_msg_dump_one(NONE, avp);
323                                        ASSERT(0); /* To check if this really happens, and understand why... */
324                                        goto next;
325                                }
326                               
327                                /* In case of multiple AVPs */
328                                if (peer->p_hdr.info.runtime.pir_realm) {
329                                        TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP");
330                                        error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
331                                        error->pei_message = "I found several Origin-Realm AVPs";
332                                        error->pei_avp = avp;
333                                        return EINVAL;
334                                }
335                               
336                                /* If the octet string contains a \0 */
337                                if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) {
338                                        error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
339                                        error->pei_message = "Your Origin-Realm contains invalid characters.";
340                                        error->pei_avp = avp;
341                                        return EINVAL;
342                                }
343                               
344                                /* Save the value */
345                                CHECK_MALLOC(  peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len )  );
346                                peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len;
347                                break;
348
349                        case AC_HOST_IP_ADDRESS: /* Host-IP-Address */
350                                if (hdr->avp_value == NULL) {
351                                        /* This is a sanity check */
352                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
353                                        fd_msg_dump_one(NONE, avp);
354                                        ASSERT(0); /* To check if this really happens, and understand why... */
355                                        goto next;
356                                }
357                                {
358                                        sSS     ss;
359
360                                        /* Get the sockaddr value */
361                                        memset(&ss, 0, sizeof(ss));
362                                        CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss),
363                                                {
364                                                        /* in case of error, assume the AVP value was wrong */
365                                                        error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
366                                                        error->pei_avp = avp;
367                                                        return EINVAL;
368                                                } );
369
370                                        /* Save this endpoint in the list as advertized */
371                                        CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) );
372                                }
373                                break;
374
375                        case AC_VENDOR_ID: /* Vendor-Id */
376                                if (hdr->avp_value == NULL) {
377                                        /* This is a sanity check */
378                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
379                                        fd_msg_dump_one(NONE, avp);
380                                        ASSERT(0); /* To check if this really happens, and understand why... */
381                                        goto next;
382                                }
383                               
384                                /* In case of multiple AVPs */
385                                if (peer->p_hdr.info.runtime.pir_vendorid) {
386                                        TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP");
387                                        error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
388                                        error->pei_message = "I found several Vendor-Id AVPs";
389                                        error->pei_avp = avp;
390                                        return EINVAL;
391                                }
392                               
393                                peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32;
394                                break;
395
396                        case AC_PRODUCT_NAME: /* Product-Name */
397                                if (hdr->avp_value == NULL) {
398                                        /* This is a sanity check */
399                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
400                                        fd_msg_dump_one(NONE, avp);
401                                        ASSERT(0); /* To check if this really happens, and understand why... */
402                                        goto next;
403                                }
404                               
405                                /* In case of multiple AVPs */
406                                if (peer->p_hdr.info.runtime.pir_prodname) {
407                                        TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP");
408                                        error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
409                                        error->pei_message = "I found several Product-Name AVPs";
410                                        error->pei_avp = avp;
411                                        return EINVAL;
412                                }
413
414                                CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 )  );
415                                memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len);
416                                break;
417
418                        case AC_ORIGIN_STATE_ID: /* Origin-State-Id */
419                                if (hdr->avp_value == NULL) {
420                                        /* This is a sanity check */
421                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
422                                        fd_msg_dump_one(NONE, avp);
423                                        ASSERT(0); /* To check if this really happens, and understand why... */
424                                        goto next;
425                                }
426                               
427                                /* In case of multiple AVPs */
428                                if (peer->p_hdr.info.runtime.pir_orstate) {
429                                        TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP");
430                                        error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES";
431                                        error->pei_message = "I found several Origin-State-Id AVPs";
432                                        error->pei_avp = avp;
433                                        return EINVAL;
434                                }
435                               
436                                peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32;
437                                break;
438
439                        case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */
440                                if (hdr->avp_value == NULL) {
441                                        /* This is a sanity check */
442                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
443                                        fd_msg_dump_one(NONE, avp);
444                                        ASSERT(0); /* To check if this really happens, and understand why... */
445                                        goto next;
446                                }
447                               
448                                TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32);
449                                /* not that it makes a difference for us...
450                                 -- if an application actually needs this info, we could save it somewhere.
451                                */
452                                break;
453
454                        case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/
455                                {
456                                        struct avp * inavp = NULL;
457                                        application_id_t aid = 0;
458                                        vendor_id_t vid = 0;
459                                        int auth = 0;
460                                        int acct = 0;
461
462                                        /* get the first child AVP */
463                                        CHECK_FCT(  fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL)  );
464
465                                        while (inavp) {
466                                                struct avp_hdr * inhdr;
467                                                CHECK_FCT(  fd_msg_avp_hdr( inavp, &inhdr )  );
468
469                                                if (inhdr->avp_flags & AVP_FLAG_VENDOR) {
470                                                        TRACE_DEBUG(FULL, "Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP");
471                                                        fd_msg_dump_one(FULL, avp);
472                                                        goto innext;
473                                                }
474
475                                                if (inhdr->avp_value == NULL) {
476                                                        /* This is a sanity check */
477                                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
478                                                        fd_msg_dump_one(NONE, avp);
479                                                        ASSERT(0); /* To check if this really happens, and understand why... */
480                                                        goto innext;
481                                                }
482                                                switch (inhdr->avp_code) {
483                                                        case AC_VENDOR_ID: /* Vendor-Id */
484                                                                vid = inhdr->avp_value->u32;
485                                                                break;
486                                                        case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
487                                                                aid = inhdr->avp_value->u32;
488                                                                auth += 1;
489                                                                break;
490                                                        case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
491                                                                aid = inhdr->avp_value->u32;
492                                                                acct += 1;
493                                                                break;
494                                                        /* ignore other AVPs */
495                                                }
496
497                                        innext:                 
498                                                /* Go to next in AVP */
499                                                CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) );
500                                        }
501                                       
502                                        if (auth + acct != 1) {
503                                                TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored");
504                                                fd_msg_dump_one(FULL, avp);
505                                                error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
506                                                error->pei_avp = avp;
507                                                return EINVAL;
508                                        } else {
509                                                /* Add an entry in the list */
510                                                CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) );
511                                        }
512                                }
513                                break;
514
515                        case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */
516                                if (hdr->avp_value == NULL) {
517                                        /* This is a sanity check */
518                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
519                                        fd_msg_dump_one(NONE, avp);
520                                        ASSERT(0); /* To check if this really happens, and understand why... */
521                                        goto next;
522                                }
523                               
524                                if (hdr->avp_value->u32 == AI_RELAY) {
525                                        peer->p_hdr.info.runtime.pir_relay = 1;
526                                } else {
527                                        CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) );
528                                }
529                                break;
530
531                        case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */
532                                if (hdr->avp_value == NULL) {
533                                        /* This is a sanity check */
534                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
535                                        fd_msg_dump_one(NONE, avp);
536                                        ASSERT(0); /* To check if this really happens, and understand why... */
537                                        goto next;
538                                }
539                               
540                                if (hdr->avp_value->u32 == AI_RELAY) {
541                                        /* Not clear if the relay application can be inside this AVP... */
542                                        peer->p_hdr.info.runtime.pir_relay = 1;
543                                } else {
544                                        CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) );
545                                }
546                                break;
547
548                        case AC_FIRMWARE_REVISION: /* Firmware-Revision */
549                                if (hdr->avp_value == NULL) {
550                                        /* This is a sanity check */
551                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
552                                        fd_msg_dump_one(NONE, avp);
553                                        ASSERT(0); /* To check if this really happens, and understand why... */
554                                        goto next;
555                                }
556                               
557                                peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32;
558                                break;
559
560                        case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */
561                                if (hdr->avp_value == NULL) {
562                                        /* This is a sanity check */
563                                        TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA");
564                                        fd_msg_dump_one(NONE, avp);
565                                        ASSERT(0); /* To check if this really happens, and understand why... */
566                                        goto next;
567                                }
568                                if (hdr->avp_value->u32 >= 32 ) {
569                                        error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE";
570                                        error->pei_message = "I don't support this Inband-Security-Id value (yet).";
571                                        error->pei_avp = avp;
572                                        return EINVAL;
573                                }
574                                peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32);
575                                break;
576                }
577
578next:                   
579                /* Go to next AVP */
580                CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) );
581        }
582       
583        return 0;
584}
585
586/* Create a CER message for sending */ 
587static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer)
588{
589        int isi_tls = 0;
590        int isi_none = 0;
591       
592        /* Find CER dictionary object and create an instance */
593        CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) );
594       
595        /* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */
596        if (!fd_cnx_getTLS(cnx)) {
597                isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */
598                isi_tls  = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD;
599        }
600       
601        /* Add the information about the local peer */
602        CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) );
603       
604        /* Done! */
605        return 0;
606}
607
608
609/* Continue with the initiator side */
610static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx)
611{
612        /* We sent a CER on the connection, set the event queue so that we receive the CEA */
613        CHECK_FCT( set_peer_cnx(peer, &cnx) );
614       
615        /* Change state and reset the timer */
616        CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) );
617        fd_psm_next_timeout(peer, 0, CEA_TIMEOUT);
618       
619        return 0;
620}
621
622/* Reject an incoming connection attempt */
623static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error)
624{
625        /* Create and send the CEA with appropriate error code */
626        CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy );
627        CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 1 ), goto destroy );
628        CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, FD_CNX_ORDERED), goto destroy );
629       
630        /* And now destroy this connection */
631destroy:
632        fd_cnx_destroy(*recv_cnx);
633        *recv_cnx = NULL;
634        if (*cer) {
635                fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER.");
636                fd_msg_free(*cer);
637                *cer = NULL;
638        }
639}
640
641/* We have established a new connection to the remote peer, send CER and eventually process the election */
642int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator)
643{
644        struct msg * cer = NULL;
645       
646        /* Send CER on the new connection */
647        CHECK_FCT( create_CER(peer, initiator, &cer) );
648        CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) );
649       
650        /* Are we doing an election ? */
651        if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) {
652                if (election_result(peer)) {
653                        /* Close initiator connection */
654                        fd_cnx_destroy(initiator);
655
656                        /* Process with the receiver side */
657                        CHECK_FCT( fd_p_ce_process_receiver(peer) );
658
659                } else {
660                        struct fd_pei pei;
661                        memset(&pei, 0, sizeof(pei));
662                        pei.pei_errcode = "ELECTION_LOST";
663
664                        /* Answer an ELECTION LOST to the receiver side */
665                        receiver_reject(&peer->p_receiver, &peer->p_cer, &pei);
666                        CHECK_FCT( to_waitcea(peer, initiator) );
667                }
668        } else {
669                /* No election (yet) */
670                CHECK_FCT( to_waitcea(peer, initiator) );
671        }
672       
673        return 0;
674}
675
676/* We have received a Capabilities Exchange message on the peer connection */
677int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer)
678{
679        uint32_t rc = 0;
680        int st;
681        struct fd_pei pei;
682       
683        TRACE_ENTRY("%p %p", msg, peer);
684        CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) );
685       
686        /* The only valid situation where we are called is in WAITCEA and we receive a CEA (we may have won an election) */
687       
688        /* Note : to implement Capabilities Update, we would need to change here */
689       
690        /* If it is a CER, just reply an error */
691        if (req) {
692                /* Create the error message */
693                CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) );
694               
695                /* Set the error code */
696                CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) );
697
698                /* msg now contains an answer message to send back */
699                CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ );
700        }
701       
702        /* If the state is not WAITCEA, just discard the message */
703        if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) {
704                if (*msg) {
705                        fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st));
706                        CHECK_FCT_DO( fd_msg_free(*msg), /* continue */);
707                        *msg = NULL;
708                }
709               
710                return 0;
711        }
712       
713        memset(&pei, 0, sizeof(pei));
714       
715        /* Save info from the CEA into the peer */
716        CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup );
717       
718        /* Dispose of the message, we don't need it anymore */
719        CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ );
720        *msg = NULL;
721       
722        /* Check the Result-Code */
723        switch (rc) {
724                case ER_DIAMETER_SUCCESS:
725                        /* No problem, we can continue */
726                        break;
727                       
728                case ER_DIAMETER_TOO_BUSY:
729                        /* Retry later */
730                        TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP DIAMETER_TOO_BUSY, will retry later.", peer->p_hdr.info.pi_diamid);
731                        fd_psm_cleanup(peer, 0);
732                        fd_psm_next_timeout(peer, 0, 300);
733                        return 0;
734               
735                case ER_ELECTION_LOST:
736                        /* Ok, just wait for a little while for the CER to be processed on the other connection. */
737                        TRACE_DEBUG(FULL, "Peer %s replied a CEA with Result-Code AVP ELECTION_LOST, waiting for events.", peer->p_hdr.info.pi_diamid);
738                        return 0;
739               
740                default:
741                        /* In any other case, we abort all attempts to connect to this peer */
742                        TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);
743                        return EINVAL;
744        }
745       
746        /* Handshake if needed, start clear otherwise */
747        if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
748                int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ;
749                /* Special case: if the peer did not send a ISI AVP */
750                if (peer->p_hdr.info.runtime.pir_isi == 0)
751                        todo = peer->p_hdr.info.config.pic_flags.sec;
752               
753                if (todo == PI_SEC_NONE) {
754                        /* Ok for clear connection */
755                        TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
756                        CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
757                } else {
758                       
759                        fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
760                        CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL),
761                                {
762                                        /* Handshake failed ...  */
763                                        fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
764                                        goto cleanup;
765                                } );
766
767                        /* Retrieve the credentials */
768                        CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
769                }
770        }
771       
772        /* Move to next state */
773        if (peer->p_flags.pf_cnx_pb) {
774                fd_psm_change_state(peer, STATE_REOPEN );
775                CHECK_FCT( fd_p_dw_reopen(peer) );
776        } else {
777                fd_psm_change_state(peer, STATE_OPEN );
778                fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
779        }
780       
781        return 0;
782       
783cleanup:
784        fd_p_ce_clear_cnx(peer, NULL);
785
786        /* Send the error to the peer */
787        CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) );
788
789        return 0;
790}
791
792/* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */
793int fd_p_ce_process_receiver(struct fd_peer * peer)
794{
795        struct fd_pei pei;
796        struct msg * msg = NULL;
797        int isi = 0;
798        int fatal = 0;
799        int tls_sync=0;
800       
801        TRACE_ENTRY("%p", peer);
802       
803        CHECK_FCT( set_peer_cnx(peer, &peer->p_receiver) );
804        msg = peer->p_cer;
805        peer->p_cer = NULL;
806       
807        memset(&pei, 0, sizeof(pei));
808       
809        /* Parse the content of the received CER */
810        CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort );
811       
812        /* Validate the realm if needed */
813        if (peer->p_hdr.info.config.pic_realm) {
814                size_t len = strlen(peer->p_hdr.info.config.pic_realm);
815                if (fd_os_almostcasecmp(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen)) {
816                        TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
817                        pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */
818                        goto error_abort;
819                }
820        }
821       
822        /* Validate the peer if needed */
823        if (peer->p_flags.pf_responder) {
824                int res = fd_peer_validate( peer );
825                if (res < 0) {
826                        TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid);
827                        pei.pei_errcode = "DIAMETER_UNKNOWN_PEER";
828                        goto error_abort;
829                }
830                CHECK_FCT( res );
831        }
832       
833        /* Check if we have common applications */
834        if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) {
835                int got_common;
836                CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) );
837                if (!got_common) {
838                        TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid);
839                        pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION";
840                        fatal = 1;
841                        goto error_abort;
842                }
843        }
844       
845        /* Do we agree on ISI ? */
846        if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
847               
848                /* In case of responder, the validate callback must have set the config.pic_flags.sec value already */
849       
850                /* First case: we are not using old mechanism: ISI are deprecated, we ignore it. */
851                if ( ! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)) {
852                        /* Just check then that the peer configuration allows for IPsec protection */
853                        if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
854                                isi = PI_SEC_NONE;
855                        } else {
856                                /* otherwise, we should have already been protected. Reject */
857                                TRACE_DEBUG(INFO, "Non TLS-protected CER/CEA exchanges are not allowed with this peer, rejecting.");
858                        }
859                } else {
860                        /* The old mechanism is allowed with this peer. Now, look into the ISI AVP values */
861                       
862                        /* In case no ISI was present anyway: */
863                        if (!peer->p_hdr.info.runtime.pir_isi) {
864                                TRACE_DEBUG(INFO, "Inband-Security-Id AVP is missing in received CER.");
865                                if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) {
866                                        isi = PI_SEC_NONE;
867                                        TRACE_DEBUG(INFO, "IPsec protection allowed by configuration, allowing this mechanism to be used.");
868                                } else {
869                                        /* otherwise, we should have already been protected. Reject */
870                                        TRACE_DEBUG(INFO, "Rejecting the peer connection (please allow IPsec here or configure TLS in the remote peer).");
871                                }
872                        } else {
873                                /* OK, the remote peer did send the ISI AVP. */
874                                if ((peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) && (peer->p_hdr.info.runtime.pir_isi & PI_SEC_NONE)) {
875                                        /* We have allowed IPsec */
876                                        isi = PI_SEC_NONE;
877                                } else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) {
878                                        /* We can agree on TLS */
879                                        isi = PI_SEC_TLS_OLD;
880                                } else {
881                                        TRACE_DEBUG(INFO, "Remote peer requested IPsec protection, but local configuration forbids it.");
882                                }
883                        }
884                }
885       
886                /* If we did not find an agreement */
887                if (!isi) {
888                        TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid);
889                        pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY";
890                        fatal = 1;
891                        goto error_abort;
892                }
893               
894                /* Do not send the ISI IPsec if we are using the new mechanism */
895                if ((isi == PI_SEC_NONE) && (! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)))
896                        isi = 0;
897        }
898       
899        /* Reply a CEA */
900        CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) );
901        CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) );
902        CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) );
903        CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) );
904       
905        /* Handshake if needed */
906        if (isi & PI_SEC_TLS_OLD) {
907                fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE);
908                CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL),
909                        {
910                                /* Handshake failed ...  */
911                                fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid);
912                                goto cleanup;
913                        } );
914               
915                /* Retrieve the credentials */
916                CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) );
917               
918                /* Call second validation callback if needed */
919                if (peer->p_cb2) {
920                        TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid);
921                        CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ),
922                                {
923                                        TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid);
924                                        CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) );
925                                        return 0;
926                                }  );
927                }
928                tls_sync = 1;
929        } else {
930                if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) {
931                        TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid);
932                        CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) );
933                }
934        }
935               
936        /* Move to OPEN or REOPEN state */
937        if (peer->p_flags.pf_cnx_pb) {
938                fd_psm_change_state(peer, STATE_REOPEN );
939                CHECK_FCT( fd_p_dw_reopen(peer) );
940        } else {
941                if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) {
942                        fd_psm_change_state(peer, STATE_OPEN_NEW );
943                        /* send DWR */
944                        CHECK_FCT( fd_p_dw_timeout(peer) );
945                } else {
946
947                        fd_psm_change_state(peer, STATE_OPEN );
948                        fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw);
949                }
950        }
951       
952        return 0;
953
954error_abort:
955        if (pei.pei_errcode) {
956                /* Send the error */
957                receiver_reject(&peer->p_cnxctx, &msg, &pei);
958        }
959       
960cleanup:
961        if (msg) {
962                fd_msg_log(FD_MSG_LOG_DROPPED, msg, "An error occurred while processing a CER.");
963                fd_msg_free(msg);
964        }
965        fd_p_ce_clear_cnx(peer, NULL);
966
967        /* Send the error to the peer */
968        CHECK_FCT( fd_event_send(peer->p_events, fatal ? FDEVP_TERMINATE : FDEVP_CNX_ERROR, 0, NULL) );
969
970        return 0;
971}
972
973/* We have received a CER on a new connection for this peer */
974int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid)
975{
976        struct fd_pei pei;
977        int cur_state = fd_peer_getstate(peer);
978        memset(&pei, 0, sizeof(pei));
979       
980        switch (cur_state) {
981                case STATE_CLOSED:
982                        peer->p_receiver = *cnx;
983                        *cnx = NULL;
984                        peer->p_cer = *msg;
985                        *msg = NULL;
986                        CHECK_FCT( fd_p_ce_process_receiver(peer) );
987                        break;
988
989                case STATE_WAITCNXACK:
990                        /* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */
991                        peer->p_receiver = *cnx;
992                        *cnx = NULL;
993                        peer->p_cer = *msg;
994                        *msg = NULL;
995                        CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) );
996                        break;
997                       
998                case STATE_WAITCEA:
999                        if (election_result(peer)) {
1000                               
1001                                /* Close initiator connection (was already set as principal) */
1002                                fd_p_ce_clear_cnx(peer, NULL);
1003                               
1004                                /* and go on with the receiver side */
1005                                peer->p_receiver = *cnx;
1006                                *cnx = NULL;
1007                                peer->p_cer = *msg;
1008                                *msg = NULL;
1009                                CHECK_FCT( fd_p_ce_process_receiver(peer) );
1010
1011                        } else {
1012
1013                                /* Answer an ELECTION LOST to the receiver side and continue */
1014                                pei.pei_errcode = "ELECTION_LOST";
1015                                pei.pei_message = "Please answer my CER instead, you won the election.";
1016                                receiver_reject(cnx, msg, &pei);
1017                        }
1018                        break;
1019
1020                default:
1021                        pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */
1022                        pei.pei_message = "Invalid state to receive a new connection attempt.";
1023                        receiver_reject(cnx, msg, &pei);
1024        }
1025                               
1026        return 0;
1027}
Note: See TracBrowser for help on using the repository browser.