Changeset 706:4ffbc9f1e922 in freeDiameter for libfdcore/p_ce.c
- Timestamp:
- Feb 9, 2011, 3:26:58 PM (13 years ago)
- Branch:
- default
- Phase:
- public
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libfdcore/p_ce.c
r688 r706 38 38 /* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */ 39 39 40 /* Compilation option:41 USE_CEA_BROADCAST42 Define this to enable sending multiple copies of the CEA in case of SCTP connection.43 This avoids a race condition when sending an application message over a different stream44 than the CEA, it might be delivered first and thus ignored.45 */46 47 40 /* Save a connection as peer's principal */ 48 41 static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx) … … 88 81 } 89 82 90 /* Election: compare the Diameter Ids , return true if the election is won */83 /* Election: compare the Diameter Ids by lexical order, return true if the election is won */ 91 84 static __inline__ int election_result(struct fd_peer * peer) 92 85 { … … 245 238 static void cleanup_remote_CE_info(struct fd_peer * peer) 246 239 { 240 /* free linked information */ 247 241 free(peer->p_hdr.info.runtime.pir_realm); 248 peer->p_hdr.info.runtime.pir_realm = NULL;249 peer->p_hdr.info.runtime.pir_vendorid = 0;250 peer->p_hdr.info.runtime.pir_orstate = 0;251 242 free(peer->p_hdr.info.runtime.pir_prodname); 252 peer->p_hdr.info.runtime.pir_prodname = NULL;253 peer->p_hdr.info.runtime.pir_firmrev = 0;254 peer->p_hdr.info.runtime.pir_relay = 0;255 peer->p_hdr.info.runtime.pir_isi = 0;256 243 while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) { 257 244 struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next; … … 259 246 free(li); 260 247 } 261 262 fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV /* Remove previously advertised endpoints */ ); 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 ); 263 258 } 264 259 265 260 /* Extract information sent by the remote peer and save it in our peer structure */ 266 static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, char ** error_code, uint32_t *rc)261 static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc) 267 262 { 268 263 struct avp * avp = NULL; … … 309 304 310 305 /* We check that the value matches what we know, otherwise disconnect the peer */ 311 /* here also, using strcasecmp on (supposed) UTF8 data might be bad idea... to be improved */312 if (strncasecmp((char *)hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid, hdr->avp_value->os.len)) {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)) { 313 308 TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", 314 309 hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid); 315 *error_code = "DIAMETER_UNKNOWN_PEER"; 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; 316 313 return EINVAL; 317 314 } … … 330 327 /* In case of multiple AVPs */ 331 328 if (peer->p_hdr.info.runtime.pir_realm) { 332 TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-Realm AVP"); 333 goto next; 334 } 335 336 /* Save the value -- we don't change the case to avoid risking breaking UTF-8 with poor tolower() impls. */ 337 CHECK_MALLOC( peer->p_hdr.info.runtime.pir_realm = calloc( hdr->avp_value->os.len + 1, 1 ) ); 338 memcpy(peer->p_hdr.info.runtime.pir_realm, hdr->avp_value->os.data, hdr->avp_value->os.len); 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; 339 347 break; 340 348 … … 352 360 /* Get the sockaddr value */ 353 361 memset(&ss, 0, sizeof(ss)); 354 CHECK_FCT( fd_msg_avp_value_interpret( avp, &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 } ); 355 369 356 370 /* Save this endpoint in the list as advertized */ … … 370 384 /* In case of multiple AVPs */ 371 385 if (peer->p_hdr.info.runtime.pir_vendorid) { 372 TRACE_DEBUG(INFO, "Ignored multiple instances of the Vendor-Id AVP"); 373 goto next; 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; 374 391 } 375 392 … … 388 405 /* In case of multiple AVPs */ 389 406 if (peer->p_hdr.info.runtime.pir_prodname) { 390 TRACE_DEBUG(INFO, "Ignored multiple instances of the Product-Name AVP"); 391 goto next; 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; 392 412 } 393 413 … … 407 427 /* In case of multiple AVPs */ 408 428 if (peer->p_hdr.info.runtime.pir_orstate) { 409 TRACE_DEBUG(INFO, "Ignored multiple instances of the Origin-State-Id AVP"); 410 goto next; 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; 411 434 } 412 435 … … 423 446 } 424 447 425 TRACE_DEBUG(FULL, "'%s' supports a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32); 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 */ 426 452 break; 427 453 … … 477 503 TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored"); 478 504 fd_msg_dump_one(FULL, avp); 505 error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; 506 error->pei_avp = avp; 507 return EINVAL; 479 508 } else { 480 509 /* Add an entry in the list */ … … 510 539 511 540 if (hdr->avp_value->u32 == AI_RELAY) { 541 /* Not clear if the relay application can be inside this AVP... */ 512 542 peer->p_hdr.info.runtime.pir_relay = 1; 513 543 } else { 514 /* Not clear if the relay application can be inside this AVP... */515 544 CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) ); 516 545 } … … 537 566 goto next; 538 567 } 539 ASSERT( hdr->avp_value->u32 < 32 ); /* if false, we have to change the code bellow */ 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 } 540 574 peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32); 541 575 break; … … 559 593 CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) ); 560 594 561 /* Do we need Inband-Security-Id AVPs ? */595 /* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */ 562 596 if (!fd_cnx_getTLS(cnx)) { 563 isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even t if the peer does not use the old mechanism*/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 */ 564 598 isi_tls = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD; 565 599 } … … 587 621 588 622 /* Reject an incoming connection attempt */ 589 static void receiver_reject(struct cnxctx * recv_cnx, struct msg ** cer, char * rescode, char * errormsg)623 static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error) 590 624 { 591 625 /* Create and send the CEA with appropriate error code */ 592 626 CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy ); 593 CHECK_FCT_DO( fd_msg_rescode_set(*cer, rescode, errormsg, NULL, 1 ), goto destroy );594 CHECK_FCT_DO( fd_out_send(cer, recv_cnx, NULL, FD_CNX_ORDERED), 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 ); 595 629 596 630 /* And now destroy this connection */ 597 631 destroy: 598 fd_cnx_destroy(recv_cnx); 632 fd_cnx_destroy(*recv_cnx); 633 *recv_cnx = NULL; 599 634 if (*cer) { 600 635 fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER."); … … 614 649 615 650 /* Are we doing an election ? */ 616 fd_cpu_flush_cache(); 617 if (peer->p_hdr.info.runtime.pir_state == STATE_WAITCNXACK_ELEC) { 651 if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) { 618 652 if (election_result(peer)) { 619 653 /* Close initiator connection */ … … 624 658 625 659 } else { 660 struct fd_pei pei; 661 memset(&pei, 0, sizeof(pei)); 662 pei.pei_errcode = "ELECTION_LOST"; 626 663 627 664 /* Answer an ELECTION LOST to the receiver side */ 628 receiver_reject(peer->p_receiver, &peer->p_cer, "ELECTION_LOST", NULL); 629 peer->p_receiver = NULL; 665 receiver_reject(&peer->p_receiver, &peer->p_cer, &pei); 630 666 CHECK_FCT( to_waitcea(peer, initiator) ); 631 667 } … … 641 677 int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer) 642 678 { 643 char * ec;644 679 uint32_t rc = 0; 680 int st; 681 struct fd_pei pei; 682 645 683 TRACE_ENTRY("%p %p", msg, peer); 646 684 CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) ); … … 656 694 657 695 /* Set the error code */ 658 CHECK_FCT( fd_msg_rescode_set(*msg, " DIAMETER_COMMAND_UNSUPPORTED", "No CER allowed in current state", NULL, 1 ) );696 CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) ); 659 697 660 698 /* msg now contains an answer message to send back */ … … 663 701 664 702 /* If the state is not WAITCEA, just discard the message */ 665 fd_cpu_flush_cache(); 666 if (req || (peer->p_hdr.info.runtime.pir_state != STATE_WAITCEA)) { 703 if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) { 667 704 if (*msg) { 668 fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR( peer->p_hdr.info.runtime.pir_state));705 fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st)); 669 706 CHECK_FCT_DO( fd_msg_free(*msg), /* continue */); 670 707 *msg = NULL; … … 674 711 } 675 712 713 memset(&pei, 0, sizeof(pei)); 714 676 715 /* Save info from the CEA into the peer */ 677 CHECK_FCT_DO( save_remote_CE_info(*msg, peer, & ec, &rc), goto cleanup );716 CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup ); 678 717 679 718 /* Dispose of the message, we don't need it anymore */ … … 701 740 default: 702 741 /* In any other case, we abort all attempts to connect to this peer */ 703 TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP%d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc);742 TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc); 704 743 return EINVAL; 705 744 } … … 751 790 } 752 791 753 /* Handle the receiver side to go to OPEN state (any election is resolved) */792 /* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */ 754 793 int fd_p_ce_process_receiver(struct fd_peer * peer) 755 794 { 756 char * ec = NULL;795 struct fd_pei pei; 757 796 struct msg * msg = NULL; 758 797 int isi = 0; 759 798 int fatal = 0; 799 int tls_sync=0; 760 800 761 801 TRACE_ENTRY("%p", peer); … … 765 805 peer->p_cer = NULL; 766 806 807 memset(&pei, 0, sizeof(pei)); 808 767 809 /* Parse the content of the received CER */ 768 CHECK_FCT_DO( save_remote_CE_info(msg, peer, &ec, NULL), goto error_abort ); 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 } 769 821 770 822 /* Validate the peer if needed */ … … 773 825 if (res < 0) { 774 826 TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid); 775 ec= "DIAMETER_UNKNOWN_PEER";827 pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; 776 828 goto error_abort; 777 829 } … … 785 837 if (!got_common) { 786 838 TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid); 787 ec= "DIAMETER_NO_COMMON_APPLICATION";839 pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION"; 788 840 fatal = 1; 789 841 goto error_abort; … … 835 887 if (!isi) { 836 888 TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid); 837 ec= "DIAMETER_NO_COMMON_SECURITY";889 pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY"; 838 890 fatal = 1; 839 891 goto error_abort; … … 849 901 CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) ); 850 902 CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) ); 851 #ifdef USE_CEA_BROADCAST852 CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, (isi & PI_SEC_TLS_OLD) ? FD_CNX_ORDERED : FD_CNX_BROADCAST) ); /* Broadcast in order to avoid further messages sent over a different stream be delivered first... */853 #else /* USE_CEA_BROADCAST */854 903 CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) ); 855 #endif /* USE_CEA_BROADCAST */856 904 857 905 /* Handshake if needed */ … … 878 926 } ); 879 927 } 880 928 tls_sync = 1; 881 929 } else { 882 930 if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { … … 891 939 CHECK_FCT( fd_p_dw_reopen(peer) ); 892 940 } else { 893 fd_psm_change_state(peer, STATE_OPEN ); 894 fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); 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 } 895 950 } 896 951 … … 898 953 899 954 error_abort: 900 if (ec) { 901 /* Create the error message */ 902 CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ) ); 903 904 /* Set the error code */ 905 CHECK_FCT( fd_msg_rescode_set(msg, ec, NULL, NULL, 1 ) ); 906 907 /* msg now contains an answer message to send back */ 908 CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ ); 955 if (pei.pei_errcode) { 956 /* Send the error */ 957 receiver_reject(&peer->p_cnxctx, &msg, &pei); 909 958 } 910 959 … … 925 974 int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid) 926 975 { 927 fd_cpu_flush_cache(); 928 switch (peer->p_hdr.info.runtime.pir_state) { 976 struct fd_pei pei; 977 int cur_state = fd_peer_getstate(peer); 978 memset(&pei, 0, sizeof(pei)); 979 980 switch (cur_state) { 929 981 case STATE_CLOSED: 930 982 peer->p_receiver = *cnx; … … 960 1012 961 1013 /* Answer an ELECTION LOST to the receiver side and continue */ 962 receiver_reject(*cnx, msg, "ELECTION_LOST", "Please answer my CER instead, you won the election."); 963 *cnx = NULL; 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); 964 1017 } 965 1018 break; 966 1019 967 1020 default: 968 receiver_reject(*cnx, msg, "DIAMETER_UNABLE_TO_COMPLY", "Invalid state to receive a new connection attempt"); 969 *cnx = NULL; 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); 970 1024 } 971 1025
Note: See TracChangeset
for help on using the changeset viewer.