Index: cdr_diameter.c =================================================================== --- cdr_diameter.c (revision 0) +++ cdr_diameter.c (revision 0) @@ -0,0 +1,376 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 16\04\09 + */ + +/*! \file + \brief Diameter CDR support. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/cdr.h" +#include "asterisk/module.h" +#include "diameter/diameter_base.h" + +/*! ISO 8601 standard format */ +#define DATE_FORMAT "%Y-%m-%d %T %z" +#define MAXPORT 65535 /*! Maximum valid port no. allowed */ + +static char *desc = "DIAMETER CDR Backend"; +static char *name = "diameter"; +static char *cdr_config = "cdr.conf"; + +enum { + /*! Log dates and times in UTC */ + DIAMETER_FLAG_USEGMTIME = (1 << 0), + /*! Log Unique ID */ + DIAMETER_FLAG_LOGUNIQUEID = (1 << 1), + /*! Log User Field */ + DIAMETER_FLAG_LOGUSERFIELD = (1 << 2) +}; + +static struct ast_flags global_flags = { DIAMETER_FLAG_USEGMTIME | DIAMETER_FLAG_LOGUNIQUEID | DIAMETER_FLAG_LOGUSERFIELD }; +static int validate_ip_address(const char *ip); +static struct diameter_cfg* get_configurationinfo(struct ast_config *cfg); + +/*! \brief Builds an avp list which contain cdr information */ +static int build_cdr_avplist(struct avp **avplist, struct ast_cdr *cdr) +{ + struct ast_tm ast_time; + char str_time[128]; + char buf[32]; + char *tmp = NULL; + + /* Account code */ + if (!build_and_add_avp(AVP_AST_ACCT_CODE, cdr->accountcode, strlen(cdr->accountcode), avplist, VENDOR_CODE, 0)) + return 0; + + /* Source */ + if (!build_and_add_avp(AVP_AST_SRC, cdr->src, strlen(cdr->src), avplist, VENDOR_CODE, 0)) + return 0; + + /* Destination */ + if (!build_and_add_avp(AVP_AST_DST, cdr->dst, strlen(cdr->dst), avplist, VENDOR_CODE, 0)) + return 0; + + /* Destination context */ + if (!build_and_add_avp(AVP_AST_DST_CTX, cdr->dcontext, strlen(cdr->dcontext), avplist, VENDOR_CODE, 0)) + return 0; + + /* Caller ID */ + if (!build_and_add_avp(AVP_AST_CLID, cdr->clid, strlen(cdr->clid), avplist, VENDOR_CODE, 0)) + return 0; + + /* Channel */ + if (!build_and_add_avp(AVP_AST_CHAN, cdr->channel, strlen(cdr->channel), avplist, VENDOR_CODE, 0)) + return 0; + + /* Destination Channel */ + if (!build_and_add_avp(AVP_AST_DST_CHAN, cdr->dstchannel, strlen(cdr->dstchannel), avplist, VENDOR_CODE, 0)) + return 0; + + /* Last Application */ + if (!build_and_add_avp(AVP_AST_LAST_APP, cdr->lastapp, strlen(cdr->lastapp), avplist, VENDOR_CODE, 0)) + return 0; + + /* Last Data */ + if (!build_and_add_avp(AVP_AST_LAST_DATA, cdr->lastdata, strlen(cdr->lastdata), avplist, VENDOR_CODE, 0)) + return 0; + + /* Start Time */ + ast_strftime(str_time, sizeof(str_time), DATE_FORMAT, + ast_localtime(&cdr->start, &ast_time, + ast_test_flag(&global_flags, DIAMETER_FLAG_USEGMTIME) ? "GMT" : NULL)); + + if (!build_and_add_avp(AVP_AST_START_TIME, str_time, strlen(str_time), avplist, VENDOR_CODE, 0)) + return 0; + + /* Answer Time */ + ast_strftime(str_time, sizeof(str_time), DATE_FORMAT, + ast_localtime(&cdr->answer, &ast_time, + ast_test_flag(&global_flags, DIAMETER_FLAG_USEGMTIME) ? "GMT" : NULL)); + + if (!build_and_add_avp(AVP_AST_ANSWER_TIME, str_time, strlen(str_time), avplist, VENDOR_CODE, 0)) + return 0; + + /* End Time */ + ast_strftime(str_time, sizeof(str_time), DATE_FORMAT, + ast_localtime(&cdr->end, &ast_time, + ast_test_flag(&global_flags, DIAMETER_FLAG_USEGMTIME) ? "GMT" : NULL)); + + if (!build_and_add_avp(AVP_AST_END_TIME, str_time, strlen(str_time), avplist, VENDOR_CODE, 0)) + return 0; + + /* Duration */ + sprintf(buf,"%ld",cdr->duration); + if (!build_and_add_avp(AVP_AST_DURATION, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + /* Billable seconds */ + sprintf(buf,"%ld",cdr->billsec); + if (!build_and_add_avp(AVP_AST_BILL_SEC, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + /* Disposition */ + tmp = ast_cdr_disp2str(cdr->disposition); + if (!build_and_add_avp(AVP_AST_DISPOSITION, tmp, strlen(tmp), avplist, VENDOR_CODE, 0)) + return 0; + + /* AMA Flags */ + tmp = ast_cdr_flags2str(cdr->amaflags); + if (!build_and_add_avp(AVP_AST_AMA_FLAGS, tmp, strlen(tmp), avplist, VENDOR_CODE, 0)) + return 0; + + if (ast_test_flag(&global_flags, DIAMETER_FLAG_LOGUNIQUEID)) { + /* Unique ID */ + if (!build_and_add_avp(AVP_AST_UNIQUE_ID, cdr->uniqueid, strlen(cdr->uniqueid), avplist, VENDOR_CODE, 0)) + return 0; + } + + if (ast_test_flag(&global_flags, DIAMETER_FLAG_LOGUSERFIELD)) { + /* append the user field */ + if (!build_and_add_avp(AVP_AST_USER_FIELD, cdr->userfield, strlen(cdr->userfield), avplist, VENDOR_CODE, 0)) + return 0; + } + + /* User Name */ + if (!build_and_add_avp(AVP_USER_NAME, cdr->channel, strlen(cdr->channel), avplist, VENDOR_CODE, 0)) + return 0; + + /* Unique ID */ + if (!build_and_add_avp(AVP_SESSION_ID, cdr->uniqueid, strlen(cdr->uniqueid), avplist, VENDOR_CODE, 0)) + return 0; + + return 1; +} + +/*! Called every time a cdr is posted by asterisk */ +static int diameter_log(struct ast_cdr *cdr) +{ + int result = 0; + struct avp *cdr_avplist = NULL; + enum avp_acct_record acct_event; + + if(!cdr) + return result; + + if (!(result = build_cdr_avplist(&cdr_avplist, cdr))) { + avplist_cleanup(cdr_avplist); + cdr_avplist = NULL; + ast_debug(1, "Failed to build the avplist which contains cdr info.\n"); + return result; + } + + if (AST_CDR_BILLING != cdr->amaflags) { + /* Sending event-type accounting information to the diameter server. */ + //acct_event = AVP_ACCT_EVENT_RECORD; + acct_event = AVP_ACCT_START_RECORD; + } else { + /*! \todo Set the acct_event to START and STOP record-type for real-time accounting */ + } + + /* Sending realtime accounting information to the diameter peer(diameter server) */ + if (!(result = send_accounting_request(cdr_avplist, acct_event))) + ast_log(LOG_ERROR, "Failed to send diameter request ACR!\n"); + + return result; +} + +/*! \brief Validates the ip address read from the configuration file */ +static int validate_ip_address(const char *ip) +{ + char tempbuf[10]; + int count = 0, bit = 0, dots = 0; + + if (!ip) + return 0; + + /* checks for dots and adjacent dots*/ + while (ip[count] != '\0') { + if (ip[count] == '.') { + if (ip[count+1] == '\0') { + goto invalid_ip; + } + dots++; + if (ip[count - 1] == ip[count]) + goto invalid_ip; + } + count++; + } + if (dots != 3) + goto invalid_ip; + + count = 0; + while (ip[count] != '\0' ) { + bit = 0; + memset(tempbuf,'\0',10); + while (ip[count]!= '.' && ip[count] != '\0') { + if (bit >= 3) + goto invalid_ip; + tempbuf[bit] = ip[count]; + /* checks for alphabetical letters */ + if (tempbuf[bit]< '0' || tempbuf[bit] > '9') { + goto invalid_ip; + } + bit++; + count++; + } + tempbuf[bit] = '\0'; + /* checks for valid no in every 8 bit ip */ + if (atoi(tempbuf) >= 256) { + goto invalid_ip; + } + if (ip[count] != '\0') + count++; + } + return 1; + +invalid_ip: + ast_log(LOG_ERROR, "Invalid ip address entered in the configuration file.\n"); + return 0; +} + +/*! \brief Gets the configuration details from the configuration file for the diameter module's configuration structure */ +static struct diameter_cfg* get_configurationinfo(struct ast_config *cfg) +{ + const char* tmp = NULL; + const char* context = "diameter"; + struct diameter_cfg* diameter_cfg = NULL; + + if (!(diameter_cfg = ast_calloc(1, sizeof(struct diameter_cfg)))) + return NULL; + + if (!((tmp = ast_variable_retrieve(cfg, context, "host_ip")) && (validate_ip_address(tmp)))){ + ast_log(LOG_ERROR, "Failed to retreive \"host_ip\" value from the configuration file.\n"); + goto ConfigReadErr; + } + ast_copy_string(diameter_cfg->host_ip, tmp, strlen(tmp)+1); + + if (!((tmp = ast_variable_retrieve(cfg, context, "host_realm")) && (diameter_cfg->host_realm = ast_calloc(1, strlen(tmp)+1)))){ + ast_log(LOG_ERROR, "Failed to retreive \"host_realm\" value from the configuration file.\n"); + goto ConfigReadErr; + } + ast_copy_string(diameter_cfg->host_realm, tmp, strlen(tmp)+1); + + if (!((tmp = ast_variable_retrieve(cfg, context, "host_productname")) && (diameter_cfg->host_productname = ast_calloc(1, strlen(tmp)+1)))){ + ast_log(LOG_ERROR, "Failed to retreive \"host_productname\" value from the configuration file.\n"); + goto ConfigReadErr; + } + ast_copy_string(diameter_cfg->host_productname, tmp, strlen(tmp)+1); + + if (!((tmp = ast_variable_retrieve(cfg, context, "peer_port")) && (atoi(tmp) > 0 && atoi(tmp) < MAXPORT))){ + ast_log(LOG_ERROR, "Failed to retreive \"peer_port\" value from the configuration file.\n"); + goto ConfigReadErr; + } + diameter_cfg->peer_port = atoi(tmp); + + if (!((tmp = ast_variable_retrieve(cfg, context, "peer_ip")) && (validate_ip_address(tmp)))){ + ast_log(LOG_ERROR, "Failed to retreive \"peer_ip\" value from the configuration file.\n"); + goto ConfigReadErr; + } + ast_copy_string(diameter_cfg->peer_ip, tmp, strlen(tmp)+1); + + if (!((tmp = ast_variable_retrieve(cfg, context, "peer_realm")) && (diameter_cfg->peer_realm = ast_calloc(1, strlen(tmp)+1)))){ + ast_log(LOG_ERROR, "Failed to retreive \"peer_realm\" value from the configuration file.\n"); + goto ConfigReadErr; + } + ast_copy_string(diameter_cfg->peer_realm, tmp, strlen(tmp)+1); + + /*! The buffering variable is kept in the configuration file to indicate that buffer storage of accounting records is taken care. + Storing the accounting records in buffer is not implemented and will be done in fuure. As of now this variable is handled only + for "yes" value. */ + if (!((tmp = ast_variable_retrieve(cfg, context, "buffering")) && (!strcmp("yes", tmp)))){ + ast_log(LOG_ERROR, "Failed to retreive \"buffering\" value from the configuration file.\n"); + goto ConfigReadErr; + } + diameter_cfg->buffer_space_avail = 1; + + return diameter_cfg; + +ConfigReadErr: + ast_log(LOG_ERROR, "Diameter module failed to read configuration file\n"); + if (diameter_cfg) { + if (diameter_cfg->host_realm) { + ast_free(diameter_cfg->host_realm); + diameter_cfg->host_realm = NULL; + } + + if (diameter_cfg->host_productname) { + ast_free(diameter_cfg->host_productname); + diameter_cfg->host_productname = NULL; + } + + if (diameter_cfg->peer_realm) { + ast_free(diameter_cfg->peer_realm); + diameter_cfg->peer_realm = NULL; + } + + ast_free(diameter_cfg); + diameter_cfg = NULL; + } + return 0; +} + +/* brief Loads the diameter module */ +static int load_module(void) +{ + struct ast_config *ast_cdr_cfg = NULL; + struct diameter_cfg* diam_config = NULL; + struct ast_flags config_flags = { 0 }; + + ast_verbose("In load_module of DIAMETER.\n"); + + if ((ast_cdr_cfg = ast_config_load(cdr_config, config_flags))) { + ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(ast_cdr_cfg, "diameter", "usegmtime")), DIAMETER_FLAG_USEGMTIME); + ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(ast_cdr_cfg, "diameter", "loguniqueid")), DIAMETER_FLAG_LOGUNIQUEID); + ast_set2_flag(&global_flags, ast_true(ast_variable_retrieve(ast_cdr_cfg, "diameter", "loguserfield")), DIAMETER_FLAG_LOGUSERFIELD); + + /*NULL return of get_configurationinfo() will be checked in diameter_init()*/ + diam_config = get_configurationinfo(ast_cdr_cfg); + + ast_config_destroy(ast_cdr_cfg); + } else { + ast_log(LOG_ERROR, "Configuration loading failed.\n"); + return AST_MODULE_LOAD_DECLINE; + } + + /* Initialising the diameter base module */ + if (!diameter_init(diam_config)) { + ast_log(LOG_ERROR, "Failed to initialise diameter base module.\n"); + diameter_destroy(); + return AST_MODULE_LOAD_DECLINE; + } + + if (-1 == ast_cdr_register(name, desc, diameter_log)) { + ast_log(LOG_ERROR, "Failed to register the callback for diameter module.\n"); + diameter_destroy(); + return AST_MODULE_LOAD_DECLINE; + } + ast_verbose("Diameter module registered successfully.\n"); + return AST_MODULE_LOAD_SUCCESS; +} + +/*! \brief Unloads the diameter module */ +static int unload_module(void) +{ + diameter_destroy(); + ast_cdr_unregister(name); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Diameter CDR Backend", + .load = load_module, + .unload = unload_module, + NULL + ); + Index: diameter/diameter_message.c =================================================================== --- diameter/diameter_message.c (revision 0) +++ diameter/diameter_message.c (revision 0) @@ -0,0 +1,867 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 12\03\09 + */ + +/*! \file + \brief Diameter message related functions. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/sched.h" +#include "diameter_base.h" +#include "diameter_transaction.h" + +#define VER_SIZE 1 /*!< Version size of a Diameter message */ +#define MESSAGE_LENGTH_SIZE 3 /*!< Message length size of a Diameter message */ +#define FLAGS_SIZE 1 /*!< Flags size of a Diameter message */ +#define COMMAND_CODE_SIZE 3 /*!< Command code size of a Diameter message */ +#define APPLICATION_ID_SIZE 4 /*!< Application Id size of a Diameter message */ +#define HOP_BY_HOP_IDENTIFIER_SIZE 4 /*!< Hop-by-Hop Id size of a Diameter message */ +#define END_TO_END_IDENTIFIER_SIZE 4 /*!< End-to-End Id size of a Diameter message */ +#define AVP_VENDOR_ID_SIZE 4 /*!< Vendor-Id size of an Avp message */ + +#define AVP_CODE_SIZE 4 /*!< Code size of the AVP */ +#define AVP_FLAGS_SIZE 1 /*!< Flags size of the AVP */ +#define AVP_LENGTH_SIZE 3 /*!< Length size of the AVP */ +#define AVP_VENDOR_ID_SIZE 4 /*!< Vendor Id size of the AVP */ + +/*! Size of the Diameter Message Header */ +#define DIAMETER_MSG_HDR_SIZE \ + (VER_SIZE + MESSAGE_LENGTH_SIZE + FLAGS_SIZE + COMMAND_CODE_SIZE +\ + APPLICATION_ID_SIZE+HOP_BY_HOP_IDENTIFIER_SIZE+END_TO_END_IDENTIFIER_SIZE) + +/*! Size of the AVP Header */ +#define AVP_HDR_SIZE(_flags_) \ + (AVP_CODE_SIZE+AVP_FLAGS_SIZE+AVP_LENGTH_SIZE+\ + AVP_VENDOR_ID_SIZE*(((_flags_)&AVP_FLAG_VENDOR_SPECIFIC)!=0) ) + +/*! The version field of the Diameter header */ +#define MESSAGE_VERSION 1 + +#define IP_ADDRESS 6 /* no. of bytes for IP address with the family(IPV4) */ +#define UNSIGNED32 4 /* no. of bytes for data if Unsigned32 type */ +#define UNSIGNED64 8 /* no. of bytes for data if Unsigned64 type */ +#define to_32x_len( _len_ ) ( (_len_)+(((_len_)&3)?4-((_len_)&3):0) ) + +/* Private functions */ +static int add_req_avps(struct message *diameter_message); +static unsigned int get_three_bytes(unsigned char** stream); +static unsigned int get_four_bytes(unsigned char** stream); +static void set_three_bytes(unsigned char** stream, int int_val); +static void set_four_bytes(unsigned char** stream, int int_val); +static void set_eight_bytes(unsigned char** stream, int int_val); +static int parse_diameter_message(unsigned char* achbuf, unsigned int buflen, struct message** message_t); + + +/*! \brief Builds a Diameter request of a specific message code. */ +int build_diameter_request(enum msg_code message_code, struct message **msg) +{ + struct message *message_to_build = NULL; + + /*This function builds diameter CER, DPR, DWR ACR requests*/ + if (!(message_to_build = (struct message *) malloc(sizeof(struct message)))) + return 0; + + message_to_build->req_avp = NULL; + message_to_build->command_code = message_code; + message_to_build->version = MESSAGE_VERSION; + + /* The R bit of the flag is set indicating the message is a Request */ + message_to_build->flags = 0x80; + + /* Set to 0 for CER,DWR and DPR and to 3 if ACR */ + if (MSG_ACR != message_code) + message_to_build->application_id = DIAMETER_COMMON_MESSAGES; + else + message_to_build->application_id = DIAMETER_BASE_ACCOUNTING; + + message_to_build->end_to_end_id = ast_random(); + message_to_build->hop_by_hop_id = ast_random(); + + /* Build and add the required avps of the message. */ + if (!add_req_avps(message_to_build)) { + ast_log(LOG_ERROR, "Failed to add the required AVPs in the request message.\n"); + free_message(message_to_build); + return 0; + } + *msg = message_to_build; + return 1; +} + +/*! \brief Builds an ACR request of Record-Type as specified by the record parameter */ +int build_diameter_acr_request(void *session_t, enum avp_acct_record record) +{ + char avp_val[32] = "\0"; + struct message *acr_message = NULL; + struct timeval currtime; + struct avp* avp_t = NULL; + struct avp* pcur = NULL; + struct avp* ptemp = NULL; + long int answertime = 0; + + if (!session_t) + return 0; + + if (!build_diameter_request(MSG_ACR, &acr_message)) { + ast_log(LOG_ERROR, "Failed to build an ACR message.\n"); + return 0; + } + + if (!(avp_t = get_session_avp(session_t))) + ast_log(LOG_ERROR, "avp_t is NULL after get_session_avp.\n"); + + /* Adding the mandatory AVPs for accouting */ + if (!build_mandatory_avps(MSG_ACR, &(acr_message->req_avp))) + goto error; + + /* \todo INTERIM records has to be incremented for subsequent ACR */ + sprintf(avp_val, "%d", get_session_interim_val(session_t)); + if (!build_and_add_avp(AVP_ACCT_RECORD_NUMBER, avp_val, strlen(avp_val), &(acr_message->req_avp), VENDOR_CODE, 0)) + goto error; + + sprintf(avp_val, "%d", record); + if (!build_and_add_avp(AVP_ACCT_RECORD_TYPE, avp_val, strlen(avp_val), &(acr_message->req_avp), VENDOR_CODE, 0)) + goto error; + + /* For Interim records the billable seconds avp is updated */ + /* \note not tested */ + if (AVP_ACCT_INTERIM_RECORD == record) { + currtime = ast_tvnow(); + while (NULL != avp_t) { + if (AVP_AST_BILL_SEC == avp_t->code) { + answertime = get_session_answertime(session_t); + sprintf(avp_val, "%ld", (currtime.tv_sec) - answertime); + if (!(avp_t->data = (char *) realloc(avp_t->data, strlen(avp_val)+1))) + goto error; + strcpy(avp_t->data, avp_val); + break; + } + avp_t = avp_t->next; + } + } + + ptemp = acr_message->req_avp; + if (NULL != (pcur = get_session_avp_clone(session_t))) { + while (ptemp->next) + ptemp = ptemp->next; + ptemp->next = pcur; + } + + if (!transaction_send_message(acr_message, transaction_init(acr_message, session_t))) + goto error; + return 1; + +error: + free_message(acr_message); + return 0; +} + +/*! +* \brief Builds a diameter response of a specific message code and sends. +* \note avp_val has value only when an Error Result Code AVP is sent in the response message built else is 0. +*/ +int build_diameter_response(enum msg_code message_code, struct message *req_msg, enum diameter_msg_result_code avp_code) +{ + struct message *res_message = NULL; + enum diameter_msg_result_code res_code = DIAMETER_SUCCESS; + char avp_val[32] = "\0"; + int res = 1; + + if (!req_msg) + return 0; + + if (!(res_message = (struct message *) malloc(sizeof(struct message)))) + return 0; + + res_message->req_avp = NULL; + res_message->application_id = req_msg->application_id; + res_message->version = MESSAGE_VERSION; + + /* Response message*/ + res_message->flags = 0x00; + + /*Error bit set if error */ + if (0 != avp_code) + res_message->flags = res_message->flags | 0x20; + + /* Set the end-to-end-id and the hop-by-hop-id same as the response's request. */ + if (req_msg->end_to_end_id) + res_message->end_to_end_id = req_msg->end_to_end_id; + + if (req_msg->hop_by_hop_id) + res_message->hop_by_hop_id = req_msg->hop_by_hop_id; + + res_message->command_code = message_code; + + /* Set the Result-Code Avp in the response. For successful response the value is DIAMETER_SUCCESS and error responses the + result code is set accordingly. */ + if (0 != avp_code) + res_code = avp_code; + + sprintf(avp_val, "%d", res_code); + if (!(res = build_and_add_avp(AVP_RESULT_CODE, avp_val, strlen(avp_val), &(res_message->req_avp), VENDOR_CODE, 0))) { + ast_log(LOG_ERROR, "Failed to build the Result-Code AVP.\n"); + goto error_free; + } + + if (!(res = add_req_avps(res_message))) { + ast_log(LOG_ERROR, "Failed to add the required AVPs in the response message.\n"); + goto error_free; + } + + if (I_OPEN == diameter_base->state || R_OPEN == diameter_base->state) { + if (!(res = process_event(SEND_MESSAGE, res_message))) + goto error_free; + } else { + if(!(res = transaction_send_message(res_message, NULL))) + goto error_free; + } + +error_free: + free_message(res_message); + return res; +} + +/*! +* \brief Convert the Diameter message to a buffer to be sent. +*/ +int message_to_buffer_nsend(struct message *diameter_message) +{ + unsigned char* buf = NULL; + unsigned char send_buf[MAX_BUFFER_SIZE]; + unsigned int data_len = 0; + struct avp* avp = NULL; + + if (!diameter_message || !diameter_message->req_avp) + return 0; + + memset(send_buf, 0, MAX_BUFFER_SIZE); + buf = send_buf; + + /* Message Length - For time being set the message length as integer size. The actual length is updated to the header at the end of function*/ + ((unsigned int*)buf)[0] = htonl(sizeof(int)); + + /* Diameter Version */ + *buf = diameter_message->version; + buf += VER_SIZE + MESSAGE_LENGTH_SIZE; + + /* Command code */ + ((unsigned int*)buf)[0] = htonl(diameter_message->command_code); + + /* Flags */ + *buf = diameter_message->flags; + buf += FLAGS_SIZE + COMMAND_CODE_SIZE; + + /* Application-ID */ + ((unsigned int*)buf)[0] = htonl(diameter_message->application_id); + buf += APPLICATION_ID_SIZE; + + /* hop by hop id */ + ((unsigned int*)buf)[0] = diameter_message->hop_by_hop_id; + buf += HOP_BY_HOP_IDENTIFIER_SIZE; + + /* end to end id */ + ((unsigned int*)buf)[0] = diameter_message->end_to_end_id; + buf += END_TO_END_IDENTIFIER_SIZE; + + avp = diameter_message->req_avp; + while (NULL != avp ) { + set_four_bytes(&buf,avp->code); + /* flags*/ + (*buf++) = (unsigned char) avp->flag; + + /* avp length */ + switch (avp->type) { + case AVP_ADDRESS_TYPE: + data_len = AVP_HDR_SIZE(avp->flag) + IP_ADDRESS; + break; + + case AVP_UTF8STRING_TYPE: + case AVP_GROUPED_TYPE: + case AVP_STRING_TYPE: + data_len = AVP_HDR_SIZE(avp->flag) + strlen(avp->data); + break; + + case AVP_UNSIGNED32_TYPE: + case AVP_INTEGER32_TYPE: + data_len = AVP_HDR_SIZE(avp->flag) + UNSIGNED32; + break; + + case AVP_UNSIGNED64_TYPE: + data_len = AVP_HDR_SIZE(avp->flag) + UNSIGNED64; + break; + + case AVP_ENUMERATED_TYPE: + data_len = AVP_HDR_SIZE(avp->flag) + 4; + break; + + default: + data_len = 0; + break; + } + if (data_len) { + set_three_bytes(&buf, data_len); + data_len = 0; + } + + /* vendor id */ + if ((avp->flag&0x80)!=0) { + set_four_bytes(&buf ,avp->vendor_id); + } + + switch (avp->type) { + case AVP_ADDRESS_TYPE: + ((unsigned int*)buf)[0] = htons(1);/* IPV4 Family */ + buf +=2; + ((unsigned int*)buf)[0] = inet_addr(avp->data); + buf +=6; + break; + + case AVP_UNSIGNED32_TYPE: + case AVP_INTEGER32_TYPE: + ((unsigned int*)buf)[0] = htonl(atoi(avp->data)); + buf += 4; + break; + + case AVP_UNSIGNED64_TYPE: + set_eight_bytes(&buf, atoi(avp->data)); + break; + + case AVP_ENUMERATED_TYPE: + ((unsigned int*)buf)[0] = htonl(atoi(avp->data)); + buf += 4; + break; + + case AVP_UTF8STRING_TYPE: + case AVP_GROUPED_TYPE: + case AVP_STRING_TYPE: + memcpy(buf, avp->data, strlen(avp->data)); + buf += to_32x_len(strlen(avp->data)); + break; + + default : + break; + } + avp = avp->next; + } + /* Message Length */ + data_len = buf - send_buf; + ((unsigned int*)send_buf)[0] = htonl(data_len); + + /* Diameter Version */ + *send_buf = diameter_message->version; + + return diameter_transport_send(send_buf, data_len); +} + +/*! +* \brief Form a diameter message from the recived buffer and validate the message. +*/ +int process_diameter_message(unsigned char* achbuffer, unsigned int buflen) +{ + struct message *message_t = NULL; + char *avpval = NULL; + enum peer_event_t peer_event = 0; + int res = 0; + int start_dwrtimer = 0; + + if (!(message_t = (struct message *) malloc(sizeof(struct message)))) + return 0; + message_t->req_avp = NULL; + + if (!(res = parse_diameter_message(achbuffer, buflen, &message_t))) { + goto Cleanup; + } + + /* Events are set based on whether i am initiator or responder of the existing connection. */ + if ((message_t->command_code != MSG_CER) && (message_t->command_code != MSG_DWR) && (message_t->command_code != MSG_DPR)) { + peer_event = (diameter_base->conn_initiator) ? (I_RCV_MESSAGE) : (R_RCV_MESSAGE); + + if (!(res = process_event(peer_event, message_t))) { + ast_log(LOG_ERROR, "Failed to process RCV_MESSAGE event.\n"); + } + else + start_dwrtimer = 1; + goto Cleanup; + } else { + /* Check if it is response message */ + if (NULL != (avpval = get_avpval(&(message_t->req_avp), AVP_RESULT_CODE))) { + /* Check if the received response is a successful response */ + if ((atoi(avpval) >= 2000) || (atoi(avpval) < 3000)) { + ast_debug(5, "The message received is a successful response.\n"); + + /* Process the received response */ + if (find_transaction(message_t, diameter_base->transactions)) { + switch (message_t->command_code) { + case MSG_CER: + peer_event = (diameter_base->conn_initiator) ? (I_RCV_CEA) : (R_RCV_CEA); + break; + + case MSG_DWR: + peer_event = (diameter_base->conn_initiator) ? (I_RCV_DWA) : (R_RCV_DWA); + break; + + case MSG_DPR: + peer_event = (diameter_base->conn_initiator) ? (I_RCV_DPA) : (R_RCV_DPA); + break; + + default: + res = handle_error_message(DIAMETER_COMMAND_UNSUPPORTED, message_t); + goto Cleanup; + + } /* end of switch */ + + res = process_event(peer_event, message_t); + if(res && (MSG_DWR == message_t->command_code)) + start_dwrtimer = 1; + + } else { + ast_log(LOG_WARNING, "The recieved message does not belong to any transaction.\n"); + } + } else { + /* Check if it is an error response */ + if ((atoi(avpval) >= 3000) && (atoi(avpval) < 6000)) { + ast_log(LOG_ERROR, "Error message received.\n"); + res = handle_error_message(atoi(avpval), message_t); + } + } + goto Cleanup; + } + + ast_debug(5, "The message received is a \"request\".\n"); + + /* Check if the received request is for local consumption and is to be processed or not */ + /* Check if Destination-Host AVP if present in the request contains the local host's identity */ + if (NULL != (avpval = get_avpval(&(message_t->req_avp), AVP_DESTINATION_HOST))) { + if (strcmp(avpval, diameter_base->fqdn)) { + ast_log(LOG_WARNING, "The destination host avp's value does not match the local host's identity and the request is not processed.\n"); + goto Cleanup; + } + /* Check if Destination-Realm AVP if present in the request contains the local host's realm */ + } else if (NULL != (avpval = get_avpval(&(message_t->req_avp), AVP_DESTINATION_REALM))) { + if (strcmp(avpval, diameter_base->config->host_realm)) { + ast_log(LOG_NOTICE, "The destination realm avp's value does not match the local realm and the request is not processed.\n"); + goto Cleanup; + } + } + + if (!message_t->req_avp) + goto Cleanup; + + /* Check if the received request message has the Orign-host and Origin-realm AVPs in them */ + if (!check_host_nrealm_presence(&message_t->req_avp)) { + res = handle_error_message(DIAMETER_MISSING_AVP, message_t); + goto Cleanup; + } + + switch (message_t->command_code) { + case MSG_CER: + if (NULL != (avpval = get_avpval(&message_t->req_avp, AVP_ORIGIN_REALM))) { + if (strcmp(avpval, diameter_base->config->peer_realm)) { + /* if the request's Origin-realm does not match my peer-realm the sender of the request id not + known to me and the connection is closed.*/ + if (1 == (res = handle_error_message(DIAMETER_UNKNOWN_PEER, message_t))) { + if (0 != update_transport_state_and_process(TRANSPORT_DOWN)) + goto Cleanup; + } + else { + goto Cleanup; + } + } + } else { + goto Cleanup; + } + if(R_OPEN == diameter_base->state || I_OPEN == diameter_base->state) + peer_event = R_RCV_CER; + else + peer_event = R_CONN_CER; + break; + + case MSG_DPR: + diameter_base->transport->disconnected = 1; + peer_event = (diameter_base->conn_initiator) ? (I_RCV_DPR) : (R_RCV_DPR); + break; + + case MSG_DWR: + peer_event = (diameter_base->conn_initiator) ? (I_RCV_DWR) : (R_RCV_DWR); + break; + + default: + if (!(res = handle_error_message(DIAMETER_COMMAND_UNSUPPORTED, message_t))) + goto Cleanup; + } + /* When a non-CEA message is recieved while waiting for CEA */ + if ((WAIT_I_CEA == diameter_base->state) && (message_t->command_code != MSG_CER)) { + if (MSG_DPR == message_t->command_code) + /* When a DPR message is recieved while waiting for CEA */ + peer_event = I_PEER_DISC; + else + peer_event = I_RCV_NON_CEA; + } + if (!(res = process_event(peer_event, message_t))) { + ast_log(LOG_ERROR, "Failed to process event.\n"); + goto Cleanup; + } + + if (MSG_DWR == message_t->command_code) { + ast_mutex_lock(&diameter_base->baselock); + if (-1 == (diameter_base->dwr_timerid = ast_sched_replace(diameter_base->dwr_timerid, diameter_base->scheduler, 1000*WAIT_TIME, monitor_connection, NULL))){ + ast_log(LOG_ERROR, "Failed to reschedule watchdog timer.\n"); + res = 0; + } + ast_mutex_unlock(&diameter_base->baselock); + goto Cleanup; + } + } + res = 1; + +Cleanup: + if (start_dwrtimer) { + if (diameter_base->dwr_timerid < 0) { + ast_mutex_lock(&diameter_base->baselock); + if (-1 == (diameter_base->dwr_timerid = ast_sched_add(diameter_base->scheduler, 1000*WAIT_TIME, monitor_connection, NULL))) { + ast_log(LOG_ERROR, "Failed to start the watchdog timer.\n"); + res = 0; + } + ast_mutex_unlock(&diameter_base->baselock); + } + } + free_message(message_t); + return res; +} + +/*! \brief Frees a diameter message */ +void free_message(struct message *msg) +{ + if (!msg) + return; + + if (msg->req_avp) { + avplist_cleanup((msg->req_avp)); + msg->req_avp = NULL; + } + free(msg); + msg = NULL; +} + +/*! \brief To parse the message into 3 bytes */ +static unsigned int get_three_bytes(unsigned char** str_data) +{ + unsigned int temp = 0; + long byte_in_stream = 0; + unsigned char* stream = *str_data; + + byte_in_stream = *stream++; /* get first byte */ + temp = (unsigned int)byte_in_stream<<16; + + byte_in_stream = *stream++; /* get second byte */ + temp |= ((unsigned int)byte_in_stream)<<8; + + byte_in_stream = *stream++; /* get third byte */ + temp |= ((unsigned int)byte_in_stream); + + *str_data = stream; + return(temp); +} + +/*! \brief To parse the message into 4 bytes */ +static unsigned int get_four_bytes(unsigned char** str_data) +{ + unsigned int temp = 0 ; + long byte_in_stream = 0; + unsigned char* stream = *str_data; + + byte_in_stream = *stream++; /* get first byte */ + temp = (unsigned int)byte_in_stream << 24; + + byte_in_stream = *stream++; /* get second byte */ + temp |= ((unsigned int)byte_in_stream)<<16; + + byte_in_stream = *stream++; /* get third byte */ + temp |= ((unsigned int)byte_in_stream)<<8; + + byte_in_stream = *stream++; /* get fourth byte */ + temp |= ((unsigned int)byte_in_stream); + + *str_data = stream; + return(temp); +} + +/*! \brief To convert the data into 3 bytes for transmission */ +static void set_three_bytes(unsigned char** str_data, int int_val) +{ + unsigned int temp = 0 ; + unsigned char* stream = *str_data; + + temp = (int_val & 0x00ff0000) >> 16; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x0000ff00) >> 8; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x000000ff); + *stream++ = (unsigned char)temp; + + *str_data = stream; +} + + +/*! \brief To convert the data into 4 bytes for transmission */ +static void set_four_bytes(unsigned char** str_data, int int_val) +{ + unsigned int temp = 0 ; + unsigned char* stream = *str_data; + + temp = (int_val & 0xff000000)>> 24; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x00ff0000) >> 16; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x0000ff00) >> 8; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x000000ff); + *stream++ = (unsigned char)temp; + + *str_data = stream; +} + + +/*! \brief To convert the AVP data into 8 bytes for transmission */ +static void set_eight_bytes(unsigned char** str_data, int int_val) +{ + unsigned int temp = 0 ; + unsigned char* stream = *str_data; + + temp = (int_val & 0xff00000000000000ULL)>> 56; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x00ff000000000000ULL) >> 48; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x0000ff0000000000ULL) >> 40; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x000000ff00000000ULL) >> 32; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x00000000ff000000ULL)>> 24; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x0000000000ff0000ULL) >> 16; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x000000000000ff00ULL) >> 8; + *stream++ = (unsigned char)temp; + + temp = (int_val & 0x00000000000000ffULL); + *stream++ = (unsigned char)temp; + + *str_data = stream; +} + + +/*! +* \brief Add the mandatory AVPs of a Diameter message. +* +* \param diameter_message The message to which the AVPS are to be added +* \retval 1 on success and 0 on failure +*/ +static int add_req_avps(struct message *diameter_message) +{ + char avp_val[32] = "\0"; + int result = 1; + + if (!diameter_message) + return 0; + + if (!build_and_add_avp(AVP_ORIGIN_HOST, diameter_base->fqdn, strlen(diameter_base->fqdn), &(diameter_message->req_avp), VENDOR_CODE, 0)) { + ast_log(LOG_ERROR, "Failed to build and add Origin-Host Avp\n"); + return 0; + } + + if (!build_and_add_avp(AVP_ORIGIN_REALM, diameter_base->config->host_realm, strlen(diameter_base->config->host_realm), &(diameter_message->req_avp), VENDOR_CODE, 0)) { + ast_log(LOG_ERROR, "Failed to build and add Origin-Realm Avp\n"); + return 0; + } + + switch (diameter_message->command_code) { + case MSG_CER: + result = build_mandatory_avps(MSG_CER, &(diameter_message->req_avp)); + break; + + case MSG_DPR: + if (diameter_message->flags & 0x80) { + sprintf(avp_val, "%d", DO_NOT_WANT_TO_TALK_TO_YOU); + result = build_and_add_avp(AVP_DISCONNECT_CAUSE, avp_val, strlen(avp_val), &(diameter_message->req_avp), VENDOR_CODE, 0); + } + break; + + default: + break; + } /* end of switch */ + return result; +} + +/*! +* \brief Convert the received buffer to Diameter message structure. +* +* \param achbuf the received buffer +* +* \return message on success else NULL +*/ +static int parse_diameter_message(unsigned char* achbuf, unsigned int buflen, struct message** message) +{ + struct message* message_t = NULL; + struct avp* avp_t = NULL; + unsigned char* msg_ptr; + unsigned char avp_flags; + unsigned int message_len = 0; + unsigned int avp_len = 0; + unsigned int avpdata_len = 0; + unsigned int avp_code = 0; + unsigned int avp_vendorid = 0; + int temp = 0; + char buf[32] = "\0"; + int error = 1; + + if (!achbuf || !buflen || !message || !(*message)) + return 0; + + message_t = *message; + msg_ptr = achbuf; + + /* Getting the Version */ + message_t->version = (unsigned char)*msg_ptr; + msg_ptr += VER_SIZE; + + /* Getting Message length */ + if (buflen < (message_len = get_three_bytes(&msg_ptr))) { + ast_log(LOG_ERROR, "Diameter message len %u bigger then buffer len %u\n",message_len, buflen); + return 0; + } + + /* Getting Command flags */ + message_t->flags = *msg_ptr; + msg_ptr += FLAGS_SIZE; + + /* Getting Command code */ + message_t->command_code = get_three_bytes(&msg_ptr); + + /* Getting Application-Id */ + message_t->application_id = get_four_bytes(&msg_ptr); + + /* Getting Hop-by-Hop-Id */ + message_t->hop_by_hop_id = *((unsigned int*)msg_ptr); + msg_ptr += HOP_BY_HOP_IDENTIFIER_SIZE; + + /* Getting End-to-End-Id */ + message_t->end_to_end_id = *((unsigned int*)msg_ptr); + msg_ptr += END_TO_END_IDENTIFIER_SIZE; + + /* diameter version supported is "1" */ + if ((0x80 & message_t->flags) && (message_t->version != 1)) { + if (!handle_error_message(DIAMETER_UNSUPPORTED_VERSION, message_t)) { + ast_log(LOG_ERROR, "The message is of version %d which is not valid ", message_t->version); + } + return 0; + } + + /* If the message header size of the received diameter request is not of standard size then throw the error response to the peer */ + if (((msg_ptr - achbuf != DIAMETER_MSG_HDR_SIZE)) && (0x80 & message_t->flags)) { + ast_debug(1, "Message received with invalid header combination.\n"); + if (!handle_error_message(DIAMETER_INVALID_HDR_BITS, message_t)) { + ast_log(LOG_ERROR, "Failed to send error response message.\n"); + } + return 0; + } + + /* Get the AVPs from the message */ + while (msg_ptr < achbuf + message_len) { + /*Avp code */ + avp_code = get_four_bytes(&msg_ptr); + + /* Avp flags*/ + avp_flags = *msg_ptr; + msg_ptr += AVP_FLAGS_SIZE; + + if (0x80 & message_t->flags) { + switch (avp_flags) { + case AVP_FLAG_MANDATORY: + case AVP_FLAG_RESERVED: + case AVP_FLAG_VENDOR_SPECIFIC: + case AVP_FLAG_END_TO_END_ENCRYPT: + break; + + default: + /* unrecognised avp flags; sending error*/ + if (!handle_error_message(DIAMETER_INVALID_AVP_BITS, message_t)) { + ast_log(LOG_ERROR, "Failed to send error response message.\n"); + } + return 0; + } + } + /*Avp length*/ + if ((avp_len = get_three_bytes(&msg_ptr)) < 1) { + ast_debug(1, "Invalid AVP length \n"); + return 0; + } + + /* Avp vendor-ID */ + if (avp_flags & 0x80) { + avp_vendorid = get_four_bytes(&msg_ptr); + } + + /* Data length*/ + avpdata_len = avp_len - AVP_HDR_SIZE(avp_flags); + + if (achbuf + message_len < msg_ptr + avpdata_len) { + ast_log(LOG_ERROR,"source buffer too short to read a whole data for AVP!\n"); + return 0; + } + + /*Create the AVP */ + if (avp_code == AVP_ACCT_RECORD_TYPE || avp_code == AVP_RESULT_CODE || avp_code == AVP_ACCT_RECORD_NUMBER) { + temp = get_four_bytes(&msg_ptr); + msg_ptr -= 4; + sprintf(buf,"%d", temp); + if (!build_and_add_avp(avp_code, buf, strlen(buf), &(message_t->req_avp), avp_vendorid, avp_flags)) { + ast_log(LOG_ERROR, "Failed to create and the AVP into the message's list.\n"); + return 0; + } + } else { + if (!build_and_add_avp(avp_code, (char *) msg_ptr, avpdata_len, &(message_t->req_avp), avp_vendorid, avp_flags)) { + ast_log(LOG_ERROR, "Failed to create and the AVP into the message's list.\n"); + return 0; + } + } + msg_ptr += to_32x_len(avpdata_len); + + } /*end of while*/ + avp_t = message_t->req_avp; + if (message_t->flags & 0x80) { + while (NULL != avp_t) { + if (AVP_FLAG_UNDEFINED == avp_t->flag) { + error = handle_error_message(DIAMETER_AVP_UNSUPPORTED, message_t); + break; + } + avp_t = avp_t->next; + } + } + return error; +} + + Index: diameter/diameter_transaction.c =================================================================== --- diameter/diameter_transaction.c (revision 0) +++ diameter/diameter_transaction.c (revision 0) @@ -0,0 +1,243 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 10\04\09 + */ + +/*! \file + \brief Transaction related routines. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/hashtab.h" +#include "asterisk/sched.h" +#include "diameter_base.h" +#include "diameter_transaction.h" + +static int retransmit_request(const void* data); + +/*! \brief Create a transaction structure for a Diameter message. */ +struct transaction_t* transaction_init(struct message* diam_message, void* pvdata) +{ + struct transaction_t* trans = NULL; + struct acct_session* session = (struct acct_session *)pvdata; + + if (!diam_message) + return NULL; + + if (!(trans = (struct transaction_t *) malloc(sizeof(struct transaction_t)))) + return NULL; + + if (diam_message->end_to_end_id) + trans->endtoend_identifier = diam_message->end_to_end_id; + + if (diam_message->hop_by_hop_id) + trans->hopbyhop_identifier = diam_message->hop_by_hop_id; + + trans->request_message = diam_message; + trans->retransmissions = 0; + trans->timer_id = -1; + trans->next = NULL; + + /*add the newly created transaction to the list */ + if (!session) { + ast_mutex_lock(&diameter_base->baselock); + if (!diameter_base->transactions) { + trans->next = diameter_base->transactions; + } + diameter_base->transactions = trans; + ast_mutex_unlock(&diameter_base->baselock); + } else { + ast_mutex_lock(&session->mutex); + if (!session->trans) { + trans->next = session->trans; + } + session->trans= trans; + ast_mutex_unlock(&session->mutex); + } + return trans; +} + +int find_transaction(struct message *msg, struct transaction_t* translist) +{ + struct transaction_t* transaction_list = NULL; + + if (!( msg && translist)) + return 0; + + transaction_list = translist; + + while (NULL != transaction_list) { + if (msg->hop_by_hop_id == transaction_list->request_message->hop_by_hop_id) { + return 1; /* found transaction */ + } + transaction_list = transaction_list->next; + } + ast_debug(5, "Transaction not found for the message.\n"); + return 0; +} + + +/*! \brief Free the transaction. */ +int free_transaction(struct message *message, struct transaction_t** trans) +{ + struct transaction_t *trans_prev = NULL; + struct transaction_t *trans_curr = NULL; + + if (!(message && trans)) + return 0; + + trans_curr = trans_prev = *trans; + + while (NULL != trans_curr) { + if (message->hop_by_hop_id == trans_curr->request_message->hop_by_hop_id) { + trans_prev->next = trans_curr->next; + if (trans_prev == trans_curr) + *trans = NULL; + trans_curr->next = NULL; + transaction_cleanup(&trans_curr); + return 1; + } + trans_prev = trans_curr; + trans_curr = trans_curr->next; + } + return 0; +} + +int transaction_send_message(struct message *message_to_send, struct transaction_t* transaction) +{ + if (!message_to_send) + return 0; + + if ((0x80 & message_to_send->flags) && !transaction) + return 0; + + if (!message_to_buffer_nsend(message_to_send)) + return 0; + + /* Start the transaction timer */ + if (transaction) { + ast_mutex_lock(&diameter_base->baselock); + if (-1 == (transaction->timer_id = ast_sched_add(diameter_base->scheduler, 1000*WAIT_TIME, retransmit_request, transaction))) { + ast_log(LOG_ERROR, "Failed to start the transaction timer.\n"); + ast_mutex_unlock(&diameter_base->baselock); + return 0; + } + if (diameter_base->dwr_timerid > 0 && message_to_send->command_code != MSG_DWR) + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->dwr_timerid); + ast_mutex_unlock(&diameter_base->baselock); + } + return 1; +} + +/*! \brief Cleans up a transaction list */ +void transaction_cleanup(struct transaction_t** trans_t) +{ + struct transaction_t* cur_trans = NULL; + struct transaction_t* trans = NULL; + + if (!trans_t) + return; + + cur_trans = *trans_t; + + while (NULL != cur_trans) { + trans = cur_trans; + cur_trans = cur_trans->next; + + if (trans->timer_id > -1) { + ast_mutex_lock(&diameter_base->baselock); + AST_SCHED_DEL(diameter_base->scheduler, trans->timer_id); + ast_mutex_unlock(&diameter_base->baselock); + } + if (trans->request_message) + free_message(trans->request_message); + + free(trans); + trans = NULL; + } +} + +/*! \brief Retransmit the request when no response is recieved after 30 seconds and if request DWR the function restarts the transport. */ +static int retransmit_request(const void* pvdata) +{ + struct transaction_t* transaction = NULL; + struct message *msg = NULL; + struct acct_session tmp; + struct acct_session* session_t = NULL; + char* sessionid = NULL; + enum msg_code messagecode; + + if (!(transaction = (struct transaction_t*)pvdata)) + return -1; + + if(!transaction->request_message) + return -1; + + messagecode = transaction->request_message->command_code; + + if (MSG_ACR == messagecode) { + if (NULL != (sessionid = get_avpval(&transaction->request_message->req_avp, AVP_SESSION_ID))) { + tmp.session_id = atoi(sessionid); + if (!(session_t = ast_hashtab_lookup(diameter_base->acct_sessionlist, &tmp))) { + return -1; + } + } + } + if (messagecode != MSG_DWR) { + if (transaction->retransmissions < MAX_RETRANSMISSIONS) { + if (MSG_ACR == messagecode) + ast_mutex_lock(&session_t->mutex); + + /* Setting the T flag high for retransmission */ + transaction->request_message->flags = (transaction->request_message->flags | 0x10); + if (!message_to_buffer_nsend(transaction->request_message)) { + if (MSG_ACR == messagecode) + ast_mutex_unlock(&session_t->mutex); + return -1; + } + transaction->retransmissions++; + if (MSG_ACR == messagecode) + ast_mutex_unlock(&session_t->mutex); + return 1; + } else { + /* Send DWR request after MAX_RETRANSMISSIONS */ + if (!build_diameter_request(MSG_DWR, &msg)) { + ast_log(LOG_ERROR, "Failed to build DWR.\n"); + return -1; + } + + if (!process_event(SEND_MESSAGE, msg)) { + ast_log(LOG_ERROR, "Failed to process the event \"SND_MESSAGE\"\n"); + return -1; + } + + if (MSG_ACR == messagecode) { + ast_hashtab_remove_this_object(diameter_base->acct_sessionlist, session_t); + cleanup_acct_session(session_t); + } + return 0; + } + } + + if ((diameter_base->state == I_OPEN) || (diameter_base->state == R_OPEN)) + update_diameterbase_state(CLOSING); + + /*scheduler timer id for this transaction is setting to -1. This is because we cannot delete this scheduled job from scheduler in transaction cleanup while restarting the transport. The return 0 of this function automatically delete the scheduled job from scheduler*/ + transaction->timer_id = -1; + if (!process_event(TIMEOUT, NULL)) { + ast_log(LOG_ERROR, "Failed to process event TIMEOUT.\n"); + } + return 0; +} + Index: diameter/diameter_avp.c =================================================================== --- diameter/diameter_avp.c (revision 0) +++ diameter/diameter_avp.c (revision 0) @@ -0,0 +1,284 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 24\03\09 + */ + +/*! \file + \brief Handling avp related routines. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/cdr.h" +#include "diameter_base.h" + +static void set_avpflag_and_type(struct avp *avp); + +/*! \brief Creates an AVP adds it into a list.*/ +int build_and_add_avp(int avp_code, const char* avp_data, unsigned int avpdata_length, struct avp** avp_list, unsigned int vendor_id, enum avp_flag avpflag_value) +{ + struct avp *avp_t = NULL; + struct avp *pavp = *avp_list; + + if (!(avp_list && avp_data)) + return 0; + + if (!(avp_t = (struct avp *) malloc(sizeof(struct avp)))) + return 0; + + if (!(avp_t->data = (char *) malloc(avpdata_length + 1))) { + free(avp_t); + avp_t = NULL; + return 0; + } + + avp_t->code = avp_code; + avp_t->flag = avpflag_value; + avp_t->vendor_id = vendor_id; + set_avpflag_and_type(avp_t); + strncpy(avp_t->data, avp_data, avpdata_length); + avp_t->data[avpdata_length] = '\0'; + avp_t->length = avpdata_length; + avp_t->next = NULL; + + /* inserting the newly created avp as the first node of the list */ + if (pavp) + avp_t->next = pavp; + pavp = avp_t; + + *avp_list = pavp; + return 1; +} + +/*! +* \brief Sets the flag and type of an AVP. +* \note The flag is set only if it is not set.If the flag of the avp already has a value it is untouched(when a received +* diameter message is parsed then the avp flag will be set already when calling this function. +* +* \param avp the avp whose flag and type is set +* +*/ +static void set_avpflag_and_type(struct avp *avp) +{ + switch (avp->code) { + case AVP_USER_NAME: + case AVP_SESSION_ID: + case AVP_DESTINATION_REALM: + case AVP_DESTINATION_HOST: + case AVP_ORIGIN_HOST: + case AVP_ORIGIN_REALM: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; /* The M bit is set indicating the AVP is mandatory.*/ + avp->type = AVP_STRING_TYPE; + break; + + case AVP_VENDOR_ID: + case AVP_RESULT_CODE: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_INTEGER32_TYPE; + break; + + case AVP_TERMINATION_CAUSE: + case AVP_DISCONNECT_CAUSE: + case AVP_ACCT_RECORD_TYPE: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_ENUMERATED_TYPE; + break; + + case AVP_AUTH_APPLICATION_ID: + case AVP_ACCT_APPLICATION_ID: + case AVP_ACCT_RECORD_NUMBER: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_UNSIGNED32_TYPE; + break; + + case AVP_HOST_IP_ADDRESS: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_ADDRESS_TYPE; + break; + + case AVP_PRODUCT_NAME: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_STRING_TYPE; + break; + + case AVP_ACCOUNTING_SUB_SESSION_ID: + if (!avp->flag) + avp->flag = AVP_FLAG_MANDATORY | avp->flag; + avp->type = AVP_UNSIGNED64_TYPE; + break; + + case AVP_FAILED: + if (!avp->flag) + avp->flag = AVP_FLAG_NONE; + avp->type = AVP_GROUPED_TYPE; + break; + + /* Asterisk specific AVPs */ + case AVP_AST_ACCT_CODE: + case AVP_AST_SRC: + case AVP_AST_DST: + case AVP_AST_DST_CTX: + case AVP_AST_CLID: + case AVP_AST_CHAN: + case AVP_AST_DST_CHAN: + case AVP_AST_LAST_APP: + case AVP_AST_LAST_DATA: + case AVP_AST_UNIQUE_ID: + case AVP_AST_USER_FIELD: + if (!avp->flag) + avp->flag = AVP_FLAG_NONE; + avp->type = AVP_STRING_TYPE; + break; + + case AVP_AST_START_TIME: + case AVP_AST_ANSWER_TIME: + case AVP_AST_END_TIME: + case AVP_AST_DURATION: + case AVP_AST_BILL_SEC: + case AVP_AST_DISPOSITION: + case AVP_AST_AMA_FLAGS: + if (!avp->flag) + avp->flag = AVP_FLAG_NONE; + avp->type = AVP_INTEGER32_TYPE; + break; + + /* If the requested AVP is not handled the following is set as flag and type to it. */ + default: + ast_debug(1, "Type and Flag for the Code is not defined.\n"); + if (avp->flag && AVP_FLAG_MANDATORY) { + avp->flag = AVP_FLAG_UNDEFINED; + avp->type = AVP_TYPE_UNDEFINED; + } + break; + } +} + +/*! \brief Builds and adds the specific mandatory avps of a diameter message.*/ +int build_mandatory_avps(int msg_code, struct avp** avplist) +{ + char* avp_val = NULL; + char buf[100] = "\0"; + + if (!avplist) + return 0; + + if (MSG_ACR == msg_code) { + avp_val = diameter_base->config->peer_realm; + if (!build_and_add_avp(AVP_DESTINATION_REALM, avp_val, strlen(avp_val), avplist, VENDOR_CODE, 0)) + return 0; + + sprintf(buf, "%d", 0); + if (!build_and_add_avp(AVP_ACCOUNTING_SUB_SESSION_ID, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + sprintf(buf, "%d", DIAMETER_BASE_ACCOUNTING); + if (!build_and_add_avp(AVP_ACCT_APPLICATION_ID, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + } else if (MSG_CER == msg_code) { + avp_val = diameter_base->config->host_ip; + if (!build_and_add_avp(AVP_HOST_IP_ADDRESS, avp_val, strlen(avp_val), avplist, VENDOR_CODE, 0)) + return 0; + + sprintf(buf, "%d", VENDOR_CODE); + if (!build_and_add_avp(AVP_VENDOR_ID, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + avp_val = diameter_base->config->host_productname; + if (!build_and_add_avp(AVP_PRODUCT_NAME, avp_val, strlen(avp_val), avplist, VENDOR_CODE, 0)) + return 0; + + sprintf(buf, "%d", DIAMETER_BASE_ACCOUNTING); + if (!build_and_add_avp(AVP_ACCT_APPLICATION_ID, buf, strlen(buf), avplist, VENDOR_CODE, 0)) + return 0; + + } else { + ast_debug(5, "The requested message is not handled.\n"); + } + return 1; +} + +/*! \brief Looks for the presence of Origin_Host and Origin_Realm Avps in an avp list.*/ +int check_host_nrealm_presence(struct avp** avp_list) +{ + struct avp* avp = *avp_list; + + if (check_avp_presence(&avp, AVP_ORIGIN_HOST)) { + if (check_avp_presence(&avp, AVP_ORIGIN_REALM)) + return 1; + } + return 0; +} + +/*! \brief Looks for an avp in an avp list. */ +int check_avp_presence(struct avp** avp_list, enum avp_code_num avpcode) +{ + struct avp *avp_t = NULL; + + avp_t = *avp_list; + while (NULL != avp_t) { + if (avpcode == avp_t->code) + return 1; + avp_t = avp_t->next; + } + return 0; +} + +/*! \brief Returns 1 if the ACCT_REALTIME_REQUIRED avp's value is same as parameter avp_val else 0. */ +int realtime_avpcheck(struct avp* avp_p , enum avp_acct_realtime avp_val) +{ + char* avpval = NULL; + + if (NULL != (avpval = get_avpval(&(avp_p), AVP_ACCT_REALTIME_REQUIRED))) { + if (avp_val == atoi(avpval)) + return 1; + } + return 0; +} + +/* !\brief Returns the data of an avp if present in an avp list else 0. */ +char* get_avpval(struct avp** avplist, enum avp_code_num avpcode) +{ + struct avp* avp_t = *avplist; + + while (avp_t != NULL) { + if (avpcode == avp_t->code) { + return avp_t->data; + } + avp_t = avp_t->next; + } + return NULL; +} + +/* !\brief Cleans up an avplist. */ +void avplist_cleanup(struct avp* avp_list) +{ + struct avp* avp_p = NULL; + + while (NULL != avp_list) { + avp_p = avp_list; + avp_list = avp_list->next; + + if (avp_p->data) { + free(avp_p->data); + avp_p->data = NULL; + } + free(avp_p); + avp_p = NULL; + } +} Index: diameter/diameter_module.tar.gz =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: diameter/diameter_module.tar.gz ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Index: diameter/diameter_session.h =================================================================== --- diameter/diameter_session.h (revision 0) +++ diameter/diameter_session.h (revision 0) @@ -0,0 +1,250 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 01\04\09 + */ + +/*! \file + \brief About diameter client(peer) events session info. + + */ + +#ifndef _DIAMETER_SESSION_H +#define _DIAMETER_SESSION_H + +#include "diameter_message.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! \brief state definitions of a Diameter Client */ +enum peer_state_t { + CLOSED = 0, /*!< Not connected */ + WAIT_CONN_ACK, /*!< Connecting - waiting for Ack */ + WAIT_I_CEA, /*!< Connecting - waiting for Capabilities Exchange Answer */ + WAIT_RETURNS, /*!< When waiting for some event is over */ + I_OPEN, /*!< Connected as initiator */ + R_OPEN, /*!< Connected as responder */ + CLOSING, /*!< Closing the connection */ + ELECT, /*!< Election to be held */ + WAIT_CONN_ACK_ELECT /*!< Waiting for Ack and election held */ +}; + +/*! \brief Diameter peer's accounting states */ +enum acct_state_t { + IDLE = 10, /*!< When nothing is done */ + OPEN, /*!< On receiving successful answers or when a new Accounting record is sent */ + PENDING_S, /*!< Waiting for answer after Accounting START record is sent */ + PENDING_I, /*!< Waiting for answer after Accounting INTERIM record is sent */ + PENDING_L, /*!< Waiting for answer after Accounting STOP record is sent */ + PENDING_E, /*!< Waiting for answer after Accounting EVENT record is sent */ + PENDING_B /*!< When stored accounting records are sent */ +}; + +/*! \brief Diameter peer's accounting events */ +/*! \note Here "realtime" refers to the value of Accounting-Realtime-Required AVP */ +enum acct_event_t { + ACCT_REQUEST_ACCESS = 200, /*!< Client or device requests access */ + ACCT_ONE_TIME_SERVICE, /*!< When accountimg EVENT record is to be sent */ + ACCT_RECORDS_IN_STORAGE, + ACCT_SUCCESSFUL_START_ANS_RECVD, /*!< Successful accounting START answer received */ + ACCT_FAILURE_TOSEND_BA_NOT_DELIVER_AND_GRANT, /*!< Failure to send and buffer space available and realtime equal to GRANT_AND_LOSE */ + ACCT_FAILURE_TOSEND_NBA_GRANT_NLOSE, /*!< Failure to send and no buffer space available and realtime equal to GRANT_AND_LOSE */ + ACCT_FAILURE_TOSEND_NBA_NOT_GRANT_NLOSE,/*!< Failure to send and no buffer space available and realtime NOT equal to GRANT_AND_LOSE */ + ACCT_FAILED_START_ANS_RECVD, /* Failed accounting START answer recived */ + ACCT_FAILED_ANSWER_NOT_GRANT_NLOSE, /* Failed accounting answer recieved but realtime NOT equal to GRANT_AND_LOSE */ + ACCT_USR_SERVICE_TERMINATED, /*!< User service terminated */ + ACCT_INTERIM_INTERVAL_ELAPSES, /* When accounting INTERIM interval elapses */ + ACCT_SUCCESSFUL_INTERIM_ANS_RECVD, /*!< Successful accounting INTERIM answer received */ + ACCT_SUCCESSFUL_EVENT_ANS_RECVD, /*!< Successful accounting INTERIM record answer received */ + ACCT_FAILURE_TOSEND_BA, /* Failure to send and buffer space available */ + ACCT_FAILURE_TOSEND_NBA, /* Failure to send and no buffer space available */ + ACCT_FAILED_EVENT_ANS_RECVD, /* Failed accounting EVENT answer received */ + ACCT_SUCCESSFUL_ACCT_ANS_RCVD, /*!< Successful accounting START answer received */ + ACCT_FAILURE_TOSEND, /* Failure to send */ + ACCT_FAILED_ACCT_ANS_RCVD, /* Failed accounting answer received */ + ACCT_SUCCESSFUL_STOP_ANS_RECVD, /*!< Successful accounting STOP answer received */ + ACCT_FAILED_STOP_ANS_RECVD, /*!< Failed accounting stop record answer received */ + ACCT_FAILED_INTERIM_ANS_RCVD_GRANT_NLOSE, /* Failed INTERIM answer received and realtime equal to GRANT_AND_LOSE */ + ACCT_FAILED_INTERIM_ANS_RCVD_NOT_GRANT_NLOSE /* Failed INTERIM answer received and realtime equal NOT to GRANT_AND_LOSE */ +}; + +/*! \brief Peer events definition */ +enum peer_event_t { + START = 101, /*!< Start connection attempt */ + STOP, /*!< Stop */ + TIMEOUT, /*!< Time-out */ + WIN_ELECTION, /*!< Winning the election */ + I_RCV_CONN_ACK, /*!< Initiator - Received connection Ack */ + I_RCV_CONN_NACK,/*!< Initiator - Received connection NAck */ + I_RCV_CER, /*!< Initiator - Receiver Capabilities Exchange Request */ + R_RCV_CER, /*!< Receiver - Receiver Capabilities Exchange Request */ + I_RCV_CEA, /*!< Initiator - Receiver Capabilities Exchange Answer */ + R_RCV_CEA, /*!< Receiver - Receiver Capabilities Exchange Answer */ + I_RCV_NON_CEA, /*!< Initiator - Received non-Capabilities Exchange Answer */ + I_RCV_DPR, /*!< Initiator - Received Disconnect Peer Request */ + R_RCV_DPR, /*!< Receiver - Received Disconnect Peer Request */ + I_RCV_DPA, /*!< Initiator - Received Disconnect Peer Answer */ + R_RCV_DPA, /*!< Received - Received Disconnect Peer Answer */ + I_RCV_DWR, /*!< Initiator - Received Diameter Watch-dog Request */ + R_RCV_DWR, /*!< Receiver - Received Diameter Watch-dog Request */ + I_RCV_DWA, /*!< Initiator - Received Diameter Watch-dog Answer */ + R_RCV_DWA, /*!< Receiver - Received Diameter Watch-dog Answer */ + R_CONN_CER, /*!< Receiver - An acknowledgement is received stating that the transport connection has been established,and the associated CER has arrived */ + SEND_MESSAGE, /*!< Send a message */ + I_RCV_MESSAGE, /*!< Initiator - Received a message other than CER, CEA, DPR, DPA, DWR or DWA */ + R_RCV_MESSAGE, /*!< Receiver - Received a message other than CER, CEA, DPR, DPA, DWR or DWA */ + I_PEER_DISC, /*!< Initiator - Peer disconnected */ + R_PEER_DISC, /*!< Responder - Peer disconnected */ +}; + +/*! \brief Peer actions definition */ +enum action_t { + SND_CONN_REQ = 20, /*!< A transport connection is initiated with the peer */ + ACCEPT, /*!< The incoming connection associated with the R-Conn-CER is accepted as the responder connection */ + PROCESS_CER, /*!< The CER associated with the R-Conn-CER is processed */ + SND_CER, /*!< A CER message is sent to the peer */ + CLEANUP, /*!< The connection is shutdown, and any local resources are freed */ + ERROR, /*!< The transport layer connection is disconnected,in response to an error condition */ + PROCESS_CEA, /*!< A received CEA is processed */ + SND_DPR, /*!< A DPR message is sent to the peer */ + SND_DPA, /*!< A DPA message is sent to the peer */ + I_DISC, /*!< The transport layer initiator connection is disconnected */ + R_DISC, /*!< The transport layer receiver connection is disconnected */ + SND_MESSAGE, /*!< A message is sent */ + SND_DWR, /*!< A DWR message is sent */ + SND_DWA, /*!< A DWA message is sent */ + PROCESS_DWR, /*!< The DWR message is serviced */ + PROCESS_DWA, /*!< The DWA message is serviced */ + PROCESS, /*!< A message is serviced */ + REJECT /*!< An incoming connection is rejected */ +}; + +/*! \brief Structure to hold the accounting session information */ +struct acct_session { + int interim_time_val; /*!< The interim interval period */ + int interim_val; /*!< Value of the current interim record */ + int acct_timer_id; /*!< Timer id of the accounting interval timer */ + long int answer_time; /*!< Time at which the call was answered */ + unsigned int session_id; /*!< Session id of the message */ + unsigned int acct_subsession_id; /*!< The accounting sub-session's id */ + ast_mutex_t mutex; /*!< Mutex for the session */ + enum acct_state_t state; /*!< Current state of the accounting sub-session */ + struct avp* avp_toadd; /*!< Asterisk cdr info Avps to be appended in the message */ + struct transaction_t* trans; /*!< Transactions of the accounting messages */ +}; + +/*! + * \brief Builds and sends responses for the error requests received and also handles the received error responses. + * + * \param error_code error message code of the error request received or message code of the error response received + * \param err_msg the error request or error response received + * + * \retval 1 on success and 0 on failure + */ +int handle_error_message(int error_code, struct message *err_msg); + +/*! + * \brief Creates a session for an accounting request of a particular record. + * + * \param avplist list of avps which contain cdr information of the call for whichthe accounting information is sent + * \param acct_rec_type record type of the accounting request to be sent + * + * \retval 1 on success and 0 on failure + */ +int send_accounting_request(struct avp* avplist, enum avp_acct_record acct_rec_type); +/*! + * \brief Implements an action triggered by an event. + * + *\param action action to take place + *\param msg message received with the event which triggered the action else NULL + * + *\retval 1 on success and 0 on failure + */ +int take_action(enum action_t action, struct message *msg); + +/*! + * \brief Updates the state of the diameter client. + * + * \param peer_state state to which the diameter client is updated to + */ +void update_diameterbase_state(enum peer_state_t peer_state); +/*! + * \brief Updates the state of the diameter client and takes the next corresponding action. + * + * \param event the event that took place + * \param msg the message that reflects the event if present else NULL + * + * \retval 1 on success and 0 on failure + */ +int process_event(enum peer_event_t peer_event, struct message *msg); + +/*! + * \brief Compare the entries in the hash table. + * + * \param session_a,session_b entried to be compared + * + * \retval 0 on match + */ +int compare_session(const struct acct_session* session_a, const struct acct_session* session_b); + +/*! + * \brief Hash function for the hash table used for sessions. + * + * \param session element which is searched in hash table + * + * \retval returns the session id of the session + */ +unsigned int get_session_hash(const struct acct_session* session); + +/*! + * \brief Gets the cdr info avplist of an accounting session. + * + * \param session accounting session whose avplist is returned + * + * returns the avplist on success else NULL + */ +struct avp* get_session_avp(struct acct_session* session); + +/*!\brief Clones the cdr info avplist of an accounting session. + * + * \param session accounting session whose avplist is cloned + * + * \returns the cloned avplist on succesd else NULL + */ +struct avp* get_session_avp_clone(struct acct_session* session); + +/*! + * \brief Gets the interim interval of an accounting session. + * \param session accounting session whose interim interval is returned + * + * \retval interim interval on success else 0 + */ +int get_session_interim_val(struct acct_session* session); + +/*! + * \brief Gets the answertime(time at which call was answered) of an accounting session. + * \param session accounting session whose answertime is returned + * + * \retval answertime on success else 0 + */ +long int get_session_answertime(struct acct_session* session); + +/*! + * \brief Frees and deletes an accounting session, + * + * \param pvdata accounting session which is cleaned. + */ +void cleanup_acct_session(void* pvdata); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /*_DIAMETER_SESSION_H */ Index: diameter/diameter_message.h =================================================================== --- diameter/diameter_message.h (revision 0) +++ diameter/diameter_message.h (revision 0) @@ -0,0 +1,114 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 12\03\09 + */ + +/*! \file + \brief Diameter message info. + + */ + +#ifndef _DIAMETER_MESSAGE_H +#define _DIAMETER_MESSAGE_H + +#include "diameter_avp.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! Messages supported by the Diameter Client */ +enum msg_code { + MSG_CER = 257, + MSG_ACR = 271, + MSG_DWR = 280, + MSG_DPR = 282 +}; + +/*! Applications supported by the Diameter client */ +enum application_name { + DIAMETER_COMMON_MESSAGES = 0, + DIAMETER_NASREQ = 1, + DIAMETER_MOBILE_IP = 2, + DIAMETER_BASE_ACCOUNTING = 3 +}; + +/*! brief Structure to hold the Diameter message */ +struct message { + int version; /*!< Version field of the Diameter message */ + int length; /*!< Message Length field of the Diameter message */ + unsigned int command_code; /*!< Command-Code field of the Diameter message */ + unsigned int application_id; /*!< Application-ID field of the Diameter message */ + unsigned int end_to_end_id; /*!< End-to-End Identifier field of the Diameter message */ + unsigned int hop_by_hop_id; /*!< Hop-by-Hop Identifier field of the Diameter message */ + struct avp* req_avp; /*!< The mandatory AVPs of the Diameter message */ + unsigned char flags; /*!< Command Flags field of the Diameter message */ +}; + +/*! + * \brief Builds a diameter request message. + * + * \param message_code the message code of the diameter message to be built + * \param msg the built request is assigned to this parameter. + * + * \retval 1 on success and 0 on failure + */ +int build_diameter_request(enum msg_code message_code, struct message **msg); + +/*! + * \brief The received Diameter message is parsed,validated and processed. + * + * \param buffer the buffer which contains the received Diameter message + * \param buflen the length of the param buffer + * + * \retval 1 on success and 0 on failure + */ +int process_diameter_message(unsigned char* achbuffer, unsigned int buflen); + +/*! + * \brief Builds an accounting diameter request message. + * + * \param session the accounting session to which the built request belongs + * \param record the accounting record of the accounting request to be built + * + * \retval 1 on success and 0 on failure + */ +int build_diameter_acr_request(void *session_t, enum avp_acct_record record); + +/*! + * \brief Builds a diameter response message + * + * \param message_code message code of the response to be built + * \param req_msg the request message for which the response is built + * \param avp_val value of the Result-Code AVP to be set in the response message + * + * \retval 1 on success and 0 on failure + */ +int build_diameter_response(enum msg_code message_code, struct message *req_msg, enum diameter_msg_result_code avp_val); + +/*! + * \brief Converts the diameter message to buffer. + * \param diameter_message the diameter message which is converted to buffer + * + * \retval 1 on success and 0 on failure + */ +int message_to_buffer_nsend(struct message *diameter_message); + +/*! +* \brief Frees the message. +* +* \param msg the message to be freed +*/ +void free_message(struct message *msg); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /*_DIAMETER_MESSAGE_H */ Index: diameter/diameter_base.c =================================================================== --- diameter/diameter_base.c (revision 0) +++ diameter/diameter_base.c (revision 0) @@ -0,0 +1,414 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 06\03\09 + */ + +/*! \file + \brief Core diameter client routines. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/hashtab.h" +#include "asterisk/sched.h" +#include "diameter_base.h" +#include "diameter_transaction.h" + +/*! \brief The max. no. of accounting sessions at any point of time */ +#define MAX_CALLS 20 + +/*! \global variable for diameter client structure.*/ +struct diameter_base *diameter_base; + +/*! \function declarations */ +static int get_fqdn(char** fqdn_s); +static unsigned int session_hash(const void* pvdata); +static int session_cmp(const void* pvdata_a, const void* pvdata_b); +static void update_transport_state(enum transport_state trans_state); + +/*! +* \brief Allocate memory and initialises the members of the diameter client structure. +* +*/ +int diameter_init(struct diameter_cfg* diam_cfg) +{ + if (!diam_cfg) + return 0; + + if (!(diameter_base = (struct diameter_base *) malloc(sizeof(struct diameter_base)))) + return 0; + if (!(diameter_base->transport = (struct diameter_peer_transport *) malloc(sizeof(struct diameter_peer_transport)))) { + ast_log(LOG_ERROR, "Failed to allocate memory for transport\n"); + return 0; + } + + ast_mutex_init(&diameter_base->baselock); + diameter_base->config = diam_cfg; + diameter_base->diameter_thread = AST_PTHREADT_NULL; + diameter_base->state = CLOSED; + diameter_base->transactions = NULL; + diameter_base->transport_timer_id = -1; + diameter_base->dwr_timerid = -1; + diameter_base->transport->clientsocket = -1; + diameter_base->transport->serversocket = -1; + diameter_base->transport->activesocket = -1; + + if (!(diameter_base->scheduler = sched_context_create())) { + ast_log(LOG_ERROR, "Failed to create scheduler context.\n"); + return 0; + } + + if (!get_fqdn(&diameter_base->fqdn)) { + /* The host ip is considered as fqdn if the system api to get hostname fails. */ + if (!(diameter_base->fqdn = (char *) realloc(diameter_base->fqdn, strlen(diameter_base->config->host_ip)+1))) + return 0; + strncpy(diameter_base->fqdn, diameter_base->config->host_ip, strlen(diameter_base->config->host_ip)+1); + diameter_base->fqdn[strlen(diameter_base->config->host_ip)] = '\0'; + } + + /* Hash table for accounting sessions. */ + if (!(diameter_base->acct_sessionlist = ast_hashtab_create(MAX_CALLS, session_cmp, NULL, NULL, session_hash, 1))) { + ast_log(LOG_ERROR, "Failed to create hash table for accounting sessions.\n"); + return 0; + } + + if (!diameter_transport_init()) { + ast_log(LOG_ERROR, "Failed to initialise transport.\n"); + return 0; + } + update_transport_state_and_process(TRANSPORT_INIT); + + process_event(START, NULL); + return 1; +} + +/*! \brief Frees the diameter_client structure. */ +void diameter_destroy() +{ + if (!diameter_base) + return; + + if (diameter_base->transport->activesocket > 0) { + /* Send Disconnect Peer Request to the diameter peer before you stop talking to him. */ + if (I_OPEN == diameter_base->state || R_OPEN == diameter_base->state) { + process_event(STOP, NULL); + } else { + take_action(SND_DPR, NULL); + } + take_action(CLEANUP, NULL); + } + + if (diameter_base->acct_sessionlist) { + ast_hashtab_destroy(diameter_base->acct_sessionlist, cleanup_acct_session); + diameter_base->acct_sessionlist = NULL; + } + if (diameter_base->transactions) { + transaction_cleanup(&diameter_base->transactions); + diameter_base->transactions = NULL; + } + + if (diameter_base->transport) { + free(diameter_base->transport); + diameter_base->transport = NULL; + } + + ast_mutex_lock(&diameter_base->baselock); + + if (diameter_base->scheduler) { + if (diameter_base->transport_timer_id > -1) + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->transport_timer_id); + + if (diameter_base->dwr_timerid > -1) + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->dwr_timerid); + + sched_context_destroy(diameter_base->scheduler); + diameter_base->scheduler = NULL; + } + ast_mutex_unlock(&diameter_base->baselock); + + if (diameter_base->config) { + if (diameter_base->config->host_realm) { + free(diameter_base->config->host_realm); + diameter_base->config->host_realm = NULL; + } + if (diameter_base->config->host_productname) { + free(diameter_base->config->host_productname); + diameter_base->config->host_productname = NULL; + } + if (diameter_base->config->peer_realm) { + free(diameter_base->config->peer_realm); + diameter_base->config->peer_realm = NULL; + } + free(diameter_base->config); + diameter_base->config = NULL; + } + + if (diameter_base->fqdn) { + free(diameter_base->fqdn); + diameter_base->fqdn = NULL; + } + + ast_mutex_destroy(&diameter_base->baselock); + + free(diameter_base); + diameter_base = NULL; +} + +/*! +* \brief Handles CEA message by looking for common applications supported by both the peers and starts a watchdog timer. +* +*/ +int handle_CEA(struct message* message_recvd) +{ + struct avp *avp_t = NULL; + int result = 1; + + if (!(message_recvd && message_recvd->req_avp)) + return 0; + + avp_t = message_recvd->req_avp; + + /* \note Check for other commonly supported applications is not done as Diameter Base accounting is the application currently used.*/ + /* \todo Support for other diameter applications and check for other diameter applications in the CEA received. */ + + if (!free_transaction(message_recvd, &diameter_base->transactions)) { + ast_log(LOG_ERROR, "Failed to free the transaction of CEA message.\n"); + return 0; + } + + /* Start the watchdog timer to monitor the peer connection */ + ast_mutex_lock(&diameter_base->baselock); + if (-1 == diameter_base->dwr_timerid) { + if (-1 == (diameter_base->dwr_timerid = ast_sched_add(diameter_base->scheduler, 1000*WAIT_TIME, monitor_connection, NULL))) { + ast_log(LOG_ERROR, "Failed to start the transaction timer.\n"); + result = 0; + } + } + ast_mutex_unlock(&diameter_base->baselock); + return result; +} + +/*! + * \brief Handles the received CER message.Checks for the commonly supported applications by both the peers.If present a suuccessful + * CEA is sent else an unsuccessful CEA(Result Code AVP with value DIAMETER_NO_COMMON_APPLICATION) is sent. + * + */ +int handle_CER(struct message* message_recvd) +{ + int res = 1; + + if (!message_recvd) + return 0; + + /* Check if the CER has mandatory Avps. */ + if (1 == (res = check_avp_presence(&message_recvd->req_avp, AVP_HOST_IP_ADDRESS))) + if (1 == (res = check_avp_presence(&message_recvd->req_avp, AVP_VENDOR_ID))) + res = check_avp_presence(&message_recvd->req_avp, AVP_PRODUCT_NAME); + + if (!res) { + /* If any of the mandatory Avps is missing send the error result and return. */ + return handle_error_message(DIAMETER_MISSING_AVP, message_recvd); + } + + /* \note Only base accounting is supported, so not checking for other application .*/ + if (!build_diameter_response(MSG_CER, message_recvd, 0)) { + ast_log(LOG_ERROR, "Failed to send CEA response.\n"); + return 0; + } + + /* Register the watchdog timer to monitor the peer connection */ + ast_mutex_lock(&diameter_base->baselock); + if (-1 == diameter_base->dwr_timerid) { + if (-1 == (diameter_base->dwr_timerid = ast_sched_add(diameter_base->scheduler, 1000*WAIT_TIME, monitor_connection, NULL))) { + ast_log(LOG_ERROR, "Failed to start the transaction timer.\n"); + res = 0; + } + } + ast_mutex_unlock(&diameter_base->baselock); + return res; +} + +/*! \brief Callback function for sending DWR at regular intervals. */ +int monitor_connection(const void *data) +{ + struct message* msg = NULL; + int ret_val = 0; + + if (-1 < diameter_base->dwr_timerid) { + if (1 == (ret_val = build_diameter_request(MSG_DWR, &msg))) + ret_val = process_event(SEND_MESSAGE, msg); + + if (!ret_val) { + ast_log(LOG_WARNING, "Failed to send DWR.\n"); + return -1; + } + } + return 1; +} + +/*! brief Cleans up the global diameter_client structure for a fresh start.*/ +int diameter_cleanup(void) +{ + if (diameter_base) { + diameter_base->transport->disconnected = 0; + diameter_base->state = CLOSED; + + if (diameter_base->transport->activesocket > 0) { + hardclose(diameter_base->transport->activesocket); + diameter_base->transport->activesocket = -1; + } + diameter_base->transport->trans_state = TRANSPORT_CLOSED; + + transaction_cleanup(&diameter_base->transactions); + diameter_base->transactions = NULL; + + ast_mutex_lock(&diameter_base->baselock); + if (diameter_base->scheduler) { + if (diameter_base->transport_timer_id > -1) + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->transport_timer_id); + + if (diameter_base->dwr_timerid > -1) + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->dwr_timerid); + } + ast_mutex_unlock(&diameter_base->baselock); + + if (diameter_base->acct_sessionlist) { + ast_hashtab_destroy(diameter_base->acct_sessionlist, cleanup_acct_session); + diameter_base->acct_sessionlist = NULL; + } + + if (!(diameter_base->acct_sessionlist = ast_hashtab_create(MAX_CALLS, session_cmp, NULL, NULL, session_hash, 1))) { + ast_log(LOG_ERROR, "Failed to create hash table for accounting sessions.\n"); + return 0; + } + diameter_base->transport->trans_state = TRANSPORT_INIT; + } + return 1; +} + +/*! \brief Change the transport state to a new state and take action accordingly. */ +int update_transport_state_and_process(enum transport_state trans_state) +{ + enum peer_event_t peer_event; + int event_exists = 0; + + switch (trans_state) { + case TRANSPORT_CLOSED: + diameter_transport_cleanup(); + update_transport_state(trans_state); + break; + + case TRANSPORT_INIT: + update_transport_state(trans_state); + break; + + case TRANSPORT_CONNECTING: + update_transport_state(trans_state); + break; + + case TRANSPORT_RECONNECT: + if (TRANSPORT_CONNECTING == diameter_base->transport->trans_state) { + /* still connecting, not yet recieved ack, the event is I_RCV_CONN_NACK. */ + peer_event = I_RCV_CONN_NACK; + event_exists = 1; + update_transport_state_and_process(TRANSPORT_INIT); + } else + update_transport_state(trans_state); + break; + + case TRANSPORT_UP: + if (0 == diameter_base->conn_initiator) { /* When you are responder of the existing connection. */ + update_transport_state(trans_state); + break; + } + + /* When the transport goes up it means connection has been accepted and the event is I_RCV_CONN_ACK. */ + if (TRANSPORT_CONNECTING == diameter_base->transport->trans_state) { + peer_event = I_RCV_CONN_ACK; + event_exists = 1; + } + update_transport_state(trans_state); + break; + + case TRANSPORT_DOWN: + ast_debug(5, "Transport state machine received transport down while it was in state:%d\n", diameter_base->transport->trans_state); + update_transport_state(trans_state); + if (0 == transport_restart()) + return 0; + break; + + default: + return 0; + } + /* If event exists process the existing event. */ + if (event_exists) { + if (!process_event(peer_event, NULL)) { + ast_log(LOG_ERROR, "Failed to establish transport connections.\n"); + return 0; + } + } + return 1; +} + +/*! \brief Gets the hostname of the host machine. + * \return 1 on success 0 on failure + */ +static int get_fqdn(char** fqdn_s) +{ + char name[128] = "\0"; + char* fqdn = NULL; + struct hostent *phostent = NULL; + struct ast_hostent hostent; + + if (-1 == (gethostname(name, sizeof(name)))) + return 0; + + if (!(phostent = ast_gethostbyname(name, &hostent))) + return 0; + + if (!(fqdn = (char *) malloc(strlen(phostent->h_name)+1))) + return 0; + + strcpy(fqdn, phostent->h_name); + *fqdn_s = fqdn; + + return 1; +} + +/*! + * \brief Compare the entries in the hash table + */ +static int session_cmp(const void* pvdata_a, const void* pvdata_b) +{ + if (!(pvdata_a && pvdata_b)) + return -1; + return(compare_session(pvdata_a, pvdata_b)); +} + +/*! + * \brief Hash function for the hash table used for sessions + */ +static unsigned int session_hash(const void* pvdata) +{ + if (!pvdata) + return -1; + return (get_session_hash(pvdata)); +} + +/*! \brief Change the transport state to a new state. */ +static void update_transport_state(enum transport_state trans_state) +{ + ast_mutex_lock(&diameter_base->baselock); + diameter_base->transport->trans_state = trans_state; + ast_mutex_unlock(&diameter_base->baselock); +} Index: diameter/diameter_transaction.h =================================================================== --- diameter/diameter_transaction.h (revision 0) +++ diameter/diameter_transaction.h (revision 0) @@ -0,0 +1,89 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 09\04\09 + */ + +/*! \file + \brief Transaction related info. + + */ + +#ifndef _DIAMETER_TRANSACTION_H +#define _DIAMETER_TRANSACTION_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! Max. no. of retransmissions allowed for a message */ +#define MAX_RETRANSMISSIONS 3 + +/*! \brief Structure to hold the Diameter transaction information */ +struct transaction_t { + unsigned int endtoend_identifier; /*!< End-to-end id of the message */ + unsigned int hopbyhop_identifier; /*!< Hop-by-hop id of the message */ + int retransmissions; /*!< No.of retransmissions of the request message */ + int timer_id; /*!< Timer id of 30 seconds timer */ + struct message* request_message; /*!< Request message */ + struct transaction_t* next; /*!< The next transaction in the transaction list */ +}; + +/*! + * \brief Creates a transaction and add it intoa list. + * + * \param diam_message the message for which the transaction is created. + * \param pvdata the session of the diameter message if is an accounting message else NULL + * + * \returns the created transaction on success else NULL + */ +struct transaction_t* transaction_init(struct message* diam_message, void* pvdata); + +/*! + * /brief Finds a message's transaction in a transaction list. + * + * /param msg message whose transaction is searched + * /param translist the list in which the transaction is searched + * + * /retval 0 if transaction found else 0 + */ +int find_transaction(struct message *msg, struct transaction_t* translist); + +/*! + * /brief Frees and deletes a transaction list. + * + * /param trans_t transaction list which is cleaned up. + */ +void transaction_cleanup(struct transaction_t** trans_t); + +/*! + * /brief Frees a messages's transaction from a list. + * + * /param message whose transaction is to be freed + * /param trans the transaction list from which the message is freed. + * + * /retval 1 on success else 0 + */ +int free_transaction(struct message *message, struct transaction_t** trans); + +/*! + * /brief Sends a diameter message and starts a timer if the message is a request. + * + * /param message_to_send message that is to be sent + * /param trans transaction of the message to be sent + * + * /retval 1 on sucess and 0 on failure + */ +int transaction_send_message(struct message *message_to_send, struct transaction_t* trans); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /*_DIAMETER_TRANSACTION_H */ + Index: diameter/diameter_avp.h =================================================================== --- diameter/diameter_avp.h (revision 0) +++ diameter/diameter_avp.h (revision 0) @@ -0,0 +1,245 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 24\03\09 + */ + +/*! \file + \brief Diameter message avp related info. + + */ + +#ifndef _DIAMETER_AVP_H +#define _DIAMETER_AVP_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define VENDOR_CODE 22736 + +/*! \brief The various AVPs supported in the Diameter message */ +enum avp_code_num { + AVP_USER_NAME = 1, + AVP_SESSION_TIMEOUT = 27, + AVP_ACCT_INTERIM_INTERVAL = 85, + AVP_HOST_IP_ADDRESS = 257, + AVP_AUTH_APPLICATION_ID = 258, + AVP_ACCT_APPLICATION_ID = 259, + AVP_VENDOR_SPECIFIC_APPLICATION_ID = 260, + AVP_REDIRECT_MAX_CACHE_TIME = 262, + AVP_SESSION_ID = 263, + AVP_ORIGIN_HOST = 264, + AVP_VENDOR_ID = 266, + AVP_RESULT_CODE = 268, + AVP_PRODUCT_NAME = 269, + AVP_DISCONNECT_CAUSE = 273, + AVP_AUTH_REQUEST_TYPE = 274, + AVP_AUTH_GRACE_PERIOD = 276, + AVP_AUTH_SESSION_STATE = 277, + AVP_ORIGIN_STATE_ID = 278, + AVP_FAILED = 279, + AVP_ERROR_MESSAGE = 281, + AVP_DESTINATION_REALM = 283, + AVP_ACCOUNTING_SUB_SESSION_ID = 287, + AVP_AUTHORIZATION_LIFETIME = 291, + AVP_DESTINATION_HOST = 293, + AVP_TERMINATION_CAUSE = 295, + AVP_ORIGIN_REALM = 296, + AVP_ACCT_RECORD_TYPE = 480, + AVP_ACCT_REALTIME_REQUIRED = 483, + AVP_ACCT_RECORD_NUMBER = 485, + /* \brief Avps which represent each member of the cdr structure */ + AVP_AST_ACCT_CODE = 301, + AVP_AST_SRC = 302, + AVP_AST_DST = 303, + AVP_AST_DST_CTX = 304, + AVP_AST_CLID = 305, + AVP_AST_CHAN = 306, + AVP_AST_DST_CHAN = 307, + AVP_AST_LAST_APP = 308, + AVP_AST_LAST_DATA = 309, + AVP_AST_START_TIME = 310, + AVP_AST_ANSWER_TIME = 311, + AVP_AST_END_TIME = 312, + AVP_AST_DURATION = 313, + AVP_AST_BILL_SEC = 314, + AVP_AST_DISPOSITION = 315, + AVP_AST_AMA_FLAGS = 316, + AVP_AST_UNIQUE_ID = 317, + AVP_AST_USER_FIELD = 318, + AVP_ACCT_SESSION_ID = 319 +}; + +/*! \brief The various Accounting Realtime AVP values */ +enum avp_acct_realtime { + DELIVER_AND_GRANT = 1, + GRANT_AND_STORE = 2, + GRANT_AND_LOSE = 3 +}; + +/*brief The various result codes returned for the response messages sent */ +enum diameter_msg_result_code { + DIAMETER_SUCCESS = 2001, + DIAMETER_APPLICATION_UNSUPPORTED = 3007, + DIAMETER_UNKNOWN_PEER = 3010, + DIAMETER_UNSUPPORTED_VERSION = 5011, + DIAMETER_COMMAND_UNSUPPORTED = 3001, + DIAMETER_UNABLE_TO_DELIVER = 3002, + DIAMETER_REALM_NOT_SERVED = 3003, + DIAMETER_INVALID_HDR_BITS = 3008, + DIAMETER_INVALID_AVP_BITS = 3009, + DIAMETER_TOO_BUSY = 3004, + DIAMETER_LOOP_DETECTED = 3005, + DIAMETER_OUT_OF_SPACE = 4002, + DIAMETER_NO_COMMON_APPLICATION = 5010, + DIAMETER_AVP_UNSUPPORTED = 5001, + DIAMETER_UNKNOWN_SESSION_ID = 5002, + DIAMETER_INVALID_AVP_VALUE = 5004, + DIAMETER_MISSING_AVP = 5005, + DIAMETER_RESOURCES_EXCEEDED = 5006, + DIAMETER_CONTRADICTING_AVPS = 5007, + DIAMETER_AVP_NOT_ALLOWED = 5008, + DIAMETER_AVP_OCCURS_TOO_MANY_TIMES = 5009, + DIAMETER_UNABLE_TO_COMPLY = 5012, + DIAMETER_INVALID_BIT_IN_HEADER = 5013, + DIAMETER_INVALID_AVP_LENGTH = 5014, + DIAMETER_INVALID_MESSAGE_LENGTH = 5015, + DIAMETER_INVALID_AVP_BIT_COMBO = 5016 +}; + +/*! \brief The various Accounting record types */ +enum avp_acct_record { + AVP_ACCT_EVENT_RECORD = 1, + AVP_ACCT_START_RECORD = 2, + AVP_ACCT_INTERIM_RECORD = 3, + AVP_ACCT_STOP_RECORD = 4 +}; + +/*! \brief The varoius disconnect causes for a DPR message */ +enum avp_disconnect_cause { + REBOOTING = 0, + BUSY = 1, + DO_NOT_WANT_TO_TALK_TO_YOU = 2 +}; + +/*! \brief The various datatypes of the AVP */ +enum avp_datatype { + AVP_TYPE_UNDEFINED = 0, + AVP_DATA_TYPE, + AVP_STRING_TYPE, + AVP_ADDRESS_TYPE, + AVP_INTEGER32_TYPE, + AVP_INTEGER64_TYPE, + AVP_TIME_TYPE, + AVP_ENUMERATED_TYPE, + AVP_UNSIGNED32_TYPE, + AVP_UNSIGNED64_TYPE, + AVP_UTF8STRING_TYPE, + AVP_GROUPED_TYPE, +}; + +/*! \brief The flags of the AVP message */ +enum avp_flag { + AVP_FLAG_NONE = 0x00, + AVP_FLAG_MANDATORY = 0x40, + AVP_FLAG_RESERVED = 0x1F, + AVP_FLAG_VENDOR_SPECIFIC = 0x80, + AVP_FLAG_END_TO_END_ENCRYPT = 0x20, + AVP_FLAG_UNDEFINED, +}; + + +/*! \brief Structure to hold the AVP data */ +struct avp { + int length; /*!< AVP length */ + unsigned int code; /*!< AVP Code */ + unsigned int vendor_id; /*!< AVP Vendor-ID */ + enum avp_flag flag; /*!< AVP Flags */ + enum avp_datatype type; /*!< AVP Data format */ + char* data; /*!< Data content of the AVP */ + struct avp* next; /*!< The next AVP to follow */ +}; + + +/*! + * \brief Build and add the built AVP to the AVP list of the Diameter message + * + * \param avp_code avp code of the AVP to be built + * \param avp_data the data part of the AVP + * \param avpdata_length the length of the AVP data + * \param avp_list the list in which the built AVP is to be inserted + * \param vendor_id vendor id of the avp + * \param avpflag_value value of the avp flag which is to be set + * + * \retval 1 on success and 0 on failure + */ +int build_and_add_avp(int avp_code, const char* avp_data, unsigned int avpdata_length, struct avp** avp_list, unsigned int vendor_id, enum avp_flag avpflag_value); + +/*! + * \brief Builds and adds the required avps of a diameter message + * + * \param msg_code message code of the diameter message whose avps are to be built + * \param avplist list where the built avps are inserted + * + * \retval 1 on success and 0 on failure + */ +int build_mandatory_avps(int msg_code, struct avp** avplist); + +/*! + * /brief Checks if Origin-Host avp and Origin-Realm avps are present in the avp list + * + * /param msgavp avplist in which the avps are looked for + * + * \retval 1 if the avps are present else 0 + */ +int check_host_nrealm_presence(struct avp** avp_list); + +/*! + * /brief Checks if a particular avp is present in an avplist + * + * /param avp_list avp list in which the avp is looked for + * /param avpcode avp code of the avp which is looked for + * + * \retval 1 if the avp is present else 0 + */ +int check_avp_presence(struct avp** avp_list, enum avp_code_num avpcode); + +/*! + * /brief Checks if the ACCT_REALTIME_REQUIRED avp's value is of a particular value in an avp list + * + * /param avp_p avp list in which the ACCT_REALTIME_REQUIRED is searched for + * /param avp_val avp value against which ACCT_REALTIME_REQUIRED avp's values is compared + * + * \retval 1 if the avp values match else 0 + */ +int realtime_avpcheck(struct avp* avp_p, enum avp_acct_realtime avp_val); + +/*! + * /brief Gets the value of an avp from an avp list + * + * /param avplist avp list in which the avp is searched + * /param avpcode avp code of the avp which is searched + * + * \return avp value if the avp is present else NULL + */ +char* get_avpval(struct avp** avplist, enum avp_code_num avpcode); + +/*! + /brief Deletes an avp list + * + * /param avp_list avp list which is removed + */ +void avplist_cleanup(struct avp* avp_list); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + +#endif /* _DIAMETER_AVP_H */ Index: diameter/diameter_transport.c =================================================================== --- diameter/diameter_transport.c (revision 0) +++ diameter/diameter_transport.c (revision 0) @@ -0,0 +1,435 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 24\03\09 + */ + +/*! \file + \brief Connection related information. + + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/sched.h" +#include "asterisk/utils.h" +#include +#include +#include +#include "diameter_base.h" + +#define DIAMETER_PORT 3868 +#define INVALID_SOCKET -1 + + +AST_MUTEX_DEFINE_STATIC(schedlock); /*!< Protecting the schedule_event thread */ + +/* Private functions */ +static int create_endpoint(void); +static int create_new_socket(void); +static int restart_connecting_socket(void); +static int re_connect(const void *data); +static void* transport_tcp_listener(void* pvdata); +static const char* transport_error_string(int err_no); + + +/*! +* \brief Creates sockets for to establish connection with the peer. +* +* \retval 1 on success and 0 on failure +*/ +int diameter_transport_init() +{ + diameter_base->transport->disconnected = 0; + diameter_base->transport->trans_state = TRANSPORT_CLOSED; + + /* create socket for making a connection with the server */ + if (!create_endpoint()) { + ast_log(LOG_ERROR, "Failed to create sockets for connection.\n"); + return 0; + } + + /* Be ready to accept incoming connections */ + /* make the server listening socket as non-blocking */ + if (-1 == listen(diameter_base->transport->serversocket, 5)) { + ast_log(LOG_ERROR, "listen error is [%s]\n", transport_error_string(errno)); + return 0; + } + + /* Thread to keep listening for incoming Diameter packetes */ + if (ast_pthread_create(&diameter_base->diameter_thread, NULL, transport_tcp_listener, NULL)) { + ast_log(LOG_ERROR, "Failed to create diameter transport TCPlistener thread\n"); + return 0; + } + return 1; +} + + +/*! +* \brief Sends message to the diameter peer(server). +* +* \param data data to be sent +* \param data_length length of the data to sent +* +* \retval 1 on success and 0 on failure +*/ +int diameter_transport_send(const unsigned char *data, int data_length) +{ + if (!data) + return 0; + + if (TRANSPORT_UP == diameter_base->transport->trans_state) { + if (-1 == send(diameter_base->transport->activesocket, data, data_length, 0)) { + ast_log(LOG_ERROR, "send error is [%s]\n", transport_error_string(errno)); + return 0; + } + } + return 1; +} + +/*! brief Makes connection attempts with the peer */ +enum transport_state diameter_transport_connection() +{ + struct sockaddr_in server_addr; + enum transport_state trans_state = TRANSPORT_CLOSED; + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr(diameter_base->config->peer_ip); + server_addr.sin_port = htons(diameter_base->config->peer_port); + + /* Receive oobdata inline */ + setoobinline(diameter_base->transport->clientsocket); + + if (TRANSPORT_INIT == diameter_base->transport->trans_state) { + if (-1 == connect(diameter_base->transport->clientsocket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) { + if (EINPROGRESS != errno) { + /* Connect failed */ + ast_log(LOG_ERROR, "Connect error is [%s]\n", transport_error_string(errno)); + return TRANSPORT_CLOSED; + } else { + /* Unblocked connection */ + trans_state = TRANSPORT_CONNECTING; + } + } else { + /* Connect successful */ + diameter_base->transport->activesocket = diameter_base->transport->clientsocket; + diameter_base->conn_initiator = 1; + trans_state = TRANSPORT_UP; + } + update_transport_state_and_process(trans_state); + } + return diameter_base->transport->trans_state; +} + +/*! +* \brief Kills the listener thread and cleans up the socket +* +* \retval 1 on success and 0 on failure +*/ +void diameter_transport_cleanup() +{ + /* Only if there is any connection alive cleanup */ + if (diameter_base->transport->trans_state >= TRANSPORT_INIT) { + if (diameter_base->transport->clientsocket > 0) { + hardclose(diameter_base->transport->clientsocket); + diameter_base->transport->clientsocket = -1; + } + + if (diameter_base->transport->serversocket > 0) { + hardclose(diameter_base->transport->serversocket); + diameter_base->transport->serversocket = -1; + } + } + diameter_base->transport->trans_state = TRANSPORT_CLOSED; + + if (AST_PTHREADT_NULL != diameter_base->diameter_thread) { + pthread_cancel(diameter_base->diameter_thread); + pthread_kill(diameter_base->diameter_thread, SIGURG); + pthread_join(diameter_base->diameter_thread, NULL); + diameter_base->diameter_thread = AST_PTHREADT_NULL; + } +} + +int register_transporttimer(void) +{ + /* Only when the peer has not disconnected try to reconnect with him */ + if (!diameter_base->transport->disconnected) { + if (-1 == diameter_base->transport_timer_id) { + /* Restarting(or starting the timer if not present) the timer for attempting to make connections at interval of 30 seconds */ + if (-1 == (diameter_base->transport_timer_id = ast_sched_add(diameter_base->scheduler, 1000*WAIT_TIME, re_connect, NULL))) { + ast_log(LOG_ERROR, "Failed to start the transport timer.\n"); + return 0; + } + } + } + return 1; +} + +/*! \brief Restarts the transport */ +int transport_restart(void) +{ + if (!diameter_cleanup()) + return 0; + + if (diameter_base->conn_initiator) /* When the existing connection is initiator. */ + if (!restart_connecting_socket()) + return 0; + + return process_event(START, NULL); +} + + +/*! + * \brief Listens for and receives the incoming Diameter packets + * + * \retval 1 on success and 0 on failure + */ +static void* transport_tcp_listener(void* pvdata) +{ + int received = 0, nfds = 0; + int valopt; + int sched_res = 0; + int connection_close = 0; + unsigned char buffer[MAX_BUFFER_SIZE] = "\0"; + socklen_t size = sizeof(struct sockaddr); + fd_set read_fd, except_fd, write_fd; + struct timeval tv; + struct sockaddr server_addr; + socklen_t lon; + enum transport_state trans_state = TRANSPORT_CLOSED; + + while(1) { + pthread_testcancel(); + sched_res = ast_sched_wait(diameter_base->scheduler); + if ((sched_res < 0) || (sched_res > 1000)) + sched_res = 1000; + + tv.tv_sec = 0; + tv.tv_usec = sched_res; + FD_ZERO(&read_fd); + FD_ZERO(&except_fd); + + if (diameter_base->transport && diameter_base->transport->activesocket > 0) { + FD_SET(diameter_base->transport->activesocket,&read_fd); + FD_SET(diameter_base->transport->activesocket,&except_fd); + if (nfds < diameter_base->transport->activesocket) + nfds = diameter_base->transport->activesocket; + } else { + FD_SET(diameter_base->transport->serversocket,&read_fd); + FD_SET(diameter_base->transport->serversocket,&except_fd); + if (nfds < diameter_base->transport->serversocket) + nfds = diameter_base->transport->serversocket; + + /* If the transport in connecting state then set for write description */ + if (TRANSPORT_CONNECTING == diameter_base->transport->trans_state) { + FD_SET(diameter_base->transport->clientsocket,&write_fd); + FD_SET(diameter_base->transport->clientsocket,&except_fd); + if (nfds < diameter_base->transport->clientsocket) + nfds = diameter_base->transport->clientsocket; + } + } + + if (select(nfds+1, &read_fd, &write_fd, &except_fd, &tv) > 0) { + if (diameter_base->transport->trans_state == TRANSPORT_INIT || diameter_base->transport->trans_state == TRANSPORT_CONNECTING) { + if (FD_ISSET(diameter_base->transport->serversocket, &read_fd)) { + if (-1 == (diameter_base->transport->activesocket = accept(diameter_base->transport->serversocket, &server_addr, &size))) { + ast_log(LOG_ERROR, "Accept error is [%s]\n", transport_error_string(errno)); + } else { + if (size != sizeof(server_addr) || server_addr.sa_family != AF_INET) { + diameter_base->transport->activesocket = -1; + } else { + diameter_base->conn_initiator = 0; + update_transport_state_and_process(TRANSPORT_UP); + + /* Deleting the transport timer when a connection is established */ + if (-1 != diameter_base->transport_timer_id) { + ast_mutex_lock(&diameter_base->baselock); + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->transport_timer_id); + diameter_base->transport_timer_id = -1; + ast_mutex_unlock(&diameter_base->baselock); + } + received = recv(diameter_base->transport->activesocket, buffer, MAX_BUFFER_SIZE-1, 0); + if (received > 0) { + buffer[received] = '\0'; + process_diameter_message(buffer, received); + } + } + } + } else if (FD_ISSET(diameter_base->transport->serversocket, &except_fd)) { + ast_log(LOG_ERROR, "Exception received in server socket\n"); + } else if (FD_ISSET(diameter_base->transport->clientsocket, &write_fd)) { + /*Checking for the connect() status for client socket*/ + lon = sizeof(int); + getsockopt(diameter_base->transport->clientsocket, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); + + if (valopt) { + ast_log(LOG_ERROR, "getsockopt error is [%s]\n", transport_error_string(errno)); + /* Connection failed, updating the status to start the reconnect scheduler */ + trans_state = TRANSPORT_RECONNECT; + } else { + diameter_base->transport->activesocket = diameter_base->transport->clientsocket; + diameter_base->conn_initiator = 1; + trans_state = TRANSPORT_UP; + /* Deleting the transport timer when a connection is established */ + if (-1 != diameter_base->transport_timer_id) { + ast_mutex_lock(&diameter_base->baselock); + AST_SCHED_DEL(diameter_base->scheduler, diameter_base->transport_timer_id); + ast_mutex_unlock(&diameter_base->baselock); + } + } + update_transport_state_and_process(trans_state); + } else if (FD_ISSET(diameter_base->transport->clientsocket, &except_fd)) { + ast_log(LOG_ERROR, "Exception received in server socket\n"); + } + } else if (TRANSPORT_UP == diameter_base->transport->trans_state) { + if (FD_ISSET(diameter_base->transport->activesocket, &read_fd)) { + received = recv(diameter_base->transport->activesocket, buffer, MAX_BUFFER_SIZE-1, 0); + + if (-1 == received) { + if (EWOULDBLOCK != errno) { + connection_close = 1; + } + } else if(0 == received) { /* If 0 bytes received means FIN received */ + connection_close = 1; + } else if(received > 0) { + buffer[received] = '\0'; + process_diameter_message(buffer, received ); + } + + if (connection_close) { + connection_close = 0; + if (INVALID_SOCKET != diameter_base->transport->activesocket) { + hardclose(diameter_base->transport->activesocket); + diameter_base->transport->activesocket = INVALID_SOCKET; + } + + if(0 == update_transport_state_and_process(TRANSPORT_DOWN)) { + ast_log(LOG_ERROR, "Failed to restart the transport\n"); + } + } + } else if (FD_ISSET(diameter_base->transport->activesocket, &except_fd)) { + ast_log(LOG_ERROR, "Exception received in packet receiving socket\n"); + } + } + } + ast_mutex_lock(&schedlock); + sched_res = ast_sched_runq(diameter_base->scheduler); + ast_mutex_unlock(&schedlock); + }/* end of while */ + return NULL; +} + +/* \brief Makes connection attempts at regular intervals when no connection exists */ +static int re_connect(const void *data) +{ + diameter_base->state = CLOSED; + if (!process_event(START, NULL)) { + ast_log(LOG_ERROR, "Failed to process event \"START\"\n"); + return -1; + } + + /* If connection is established then we will remove this timer */ + return !(TRANSPORT_UP == diameter_base->transport->trans_state); +} + +static int restart_connecting_socket(void) +{ + int sock_fd = 0; + + if (-1 == (sock_fd = create_new_socket())) + return 0; + + diameter_base->transport->clientsocket = sock_fd; + return 1; +} + +static const char* transport_error_string(int err_no) +{ + #define errno_to_str(a, b) case a: return "Socket error " #a ": " b; + + switch(err_no) { + errno_to_str(EALREADY, "The requested operation was already completed"); + errno_to_str(EINPROGRESS, "A blocking call is in progress"); + errno_to_str(EWOULDBLOCK, "Operation would block"); + errno_to_str(ENOTSOCK, "The descriptor is not a socket"); + errno_to_str(EDESTADDRREQ, "A destination address is required in the function call"); + errno_to_str(EMSGSIZE, "The datagram was too large to fit in a single buffer and was truncated"); + errno_to_str(EPROTOTYPE, "The specified port is the wrong type for this socket"); + errno_to_str(ENOPROTOOPT, "The protocol option is unknown or unsupported"); + errno_to_str(EPROTONOSUPPORT, "The requested protocol is not supported"); + errno_to_str(ESOCKTNOSUPPORT, "The specified socket type is not supported in this address family"); + errno_to_str(EOPNOTSUPP, "The socket option is not supported on this socket"); + errno_to_str(EPFNOSUPPORT, "The requested Protocol Family is not supported for this socket"); + errno_to_str(EAFNOSUPPORT, "The requested Address Family is not supported for this socket"); + errno_to_str(EADDRINUSE, "The requested port/address is already in use"); + errno_to_str(EADDRNOTAVAIL, "The requested address is not available"); + errno_to_str(ENETDOWN, "Network subsystem is down"); + errno_to_str(ENETUNREACH, "The remote network is unreachable"); + errno_to_str(ENETRESET, "WinSock has dropped the connection"); + errno_to_str(ECONNABORTED, "The connection was dropped due to timeout or other failure"); + errno_to_str(ECONNRESET, "The connection has been reset by the remote peer"); + errno_to_str(ENOBUFS, "No buffer space is available"); + errno_to_str(EISCONN, "The specified socket is already connected"); + errno_to_str(ENOTCONN, "The specified socket is not connected"); + errno_to_str(ESHUTDOWN, "The specified socket has already been shut down"); + errno_to_str(ETIMEDOUT, "The connection attempt has timed out"); + errno_to_str(ECONNREFUSED, "The connection attempt was forcefully refused"); + errno_to_str(ENAMETOOLONG, "The specified name was too long"); + errno_to_str(EHOSTDOWN, "The remote host has gone down"); + errno_to_str(EHOSTUNREACH, "The remote host is unreachable"); + + default: + return "Socket error: An unknown socket error has occured"; + } + #undef errno_to_str +} + +/* \brief Creates a new socket */ +static int create_new_socket(void) +{ + int sock_fd = 0; + int flags = 0; + + if ((sock_fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0 ) { + ast_log(LOG_ERROR, "Socket create error is [%s]\n", transport_error_string (errno)); + return -1; + } + flags = fcntl(sock_fd, F_GETFL); + fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK); + return sock_fd; +} + +/* \brief Creates sockets for connecting and accepting connection.*/ +static int create_endpoint(void) +{ + struct sockaddr_in peer_addr; + int sock_fd = -1; + + if (-1 == (sock_fd = create_new_socket())) + return 0; + + diameter_base->transport->clientsocket = sock_fd; + + if (-1 == (sock_fd = create_new_socket())) + return 0; + + peer_addr.sin_family = AF_INET; + peer_addr.sin_addr.s_addr = inet_addr(diameter_base->config->host_ip); + peer_addr.sin_port = htons(DIAMETER_PORT); + + if (-1 == bind(sock_fd, (struct sockaddr *)&peer_addr, sizeof(struct sockaddr))) { + ast_log(LOG_ERROR, "Bind error is [%s]\n", transport_error_string(errno)); + return 0; + } + + diameter_base->transport->serversocket = sock_fd; + return 1; +} + Index: diameter/diameter_base.h =================================================================== --- diameter/diameter_base.h (revision 0) +++ diameter/diameter_base.h (revision 0) @@ -0,0 +1,131 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 04\03\09 + */ + +/*! \file + \brief Contains diameter client information. + + */ + +#ifndef _DIAMETER_BASE_H +#define _DIAMETER_BASE_H + + +#include "diameter_session.h" +#include "diameter_transport.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/*! \brief Interval used for all the timers used(transport reconnect timer, watchdog timer and transaction timer for requests).*/ +#define WAIT_TIME 30 + +/*! \brief Diameter configuation values */ +struct diameter_cfg { + int host_port; /*!< TCP port of the diameter client */ + int peer_port; /*!< port no. of the diameter server */ + int buffer_space_avail; /*!< buffer space used for storing accounting records (NOT _USED)*/ + char* host_realm; /*!< Realm of the diameter client */ + char* host_productname; /*!< Vendor assigned name of the product */ + char* peer_realm; /*!< realm of the diameter server */ + char host_ip[16]; /*!< ip address of the host machine */ + char peer_ip[16]; /*!< ip address of the diameter server */ +}; + + +/*! \brief Diameter applications which use this implementation need to register the following callbacks */ +/* \note Is kept for future use and is not used anywhere. */ +struct application { + enum application_name application_identifier; /*! Application identifier */ + int (*report_event)(int application_id, int event); /*! call back function to report events */ + struct application* next; +}; + +/*! \brief Global Information used by the Diameter client */ +struct diameter_base { + int conn_initiator; /*!< Indicates whether you are initiator or responder of the connection(1 if initiator and 0 if responder. */ + int dwr_timerid; /*!< Timer id of the Watchdog timer */ + int transport_timer_id; /*!< Timer id of the transport timer */ + enum peer_state_t state; /*!< Current state of the diameter client */ + enum application_name application; /*!< Diameter application supported by both the peers*/ + pthread_t diameter_thread; /*!< Thread to listen to diameter packets */ + ast_mutex_t baselock; + char* fqdn; /*!< FQDN of the host */ + struct diameter_cfg* config; /*!< Configuration info */ + struct sched_context* scheduler; /*!< Scheduler for scheduling events at regular intervals of time */ + struct transaction_t* transactions; /*!< transactions of CER, DWR, DPR */ + struct ast_hashtab* acct_sessionlist; /*!< Hash table for accounting sessions */ + struct diameter_peer_transport* transport; /*!< Connection info */ + /*struct application* application_registered; */ /*!< List of applications supported */ +}; + +/*! \global variable for diameter client structure.*/ +extern struct diameter_base *diameter_base; + +/*! + * /brief Allocates memory and initialises the members of the global diameter_base structure + * + * /param diam_conf configuration details read from the configuration file + * + * \retval 1 on success and 0 on failure + */ +int diameter_init(struct diameter_cfg *diam_conf); + +/*! + * /brief Frees and removes up the global diameter_base structure + */ +void diameter_destroy(void); + +/*! + * /brief Cleans up the global diameter_base structure for a fresh start( or restart) + * + * \retval 1 on success and 0 on failure + */ +int diameter_cleanup(void); + +/*! + * /brief Processes the received CEA message. + * + * /param message_recvd the recived CEA message from the peer + * + * \retval 1 on success and 0 on failure + */ +int handle_CEA(struct message* message_recvd); + +/*! + * /brief Processes the received CER message and responds by sending CEA response to the peer. + * + * /param message_recvd the recived CER message from the peer + * + * \retval 1 on success and 0 on failure + */ +int handle_CER(struct message* message_recvd); + +/*! + * /brief Callback function function for watchdog timer.Sends DWR at regular intervals. + * + * /param pvdata is NULL + */ +int monitor_connection(const void *pvdata); +/*! + * /brief Updated the transport state and takes the corresponding action. + * + * /param trans_state state to which the transport is updated to. + * + * \retval 1 on success and 0 on failure + */ +int update_transport_state_and_process(enum transport_state trans_state); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _DIAMETER_BASE_H */ Index: diameter/diameter_transport.h =================================================================== --- diameter/diameter_transport.h (revision 0) +++ diameter/diameter_transport.h (revision 0) @@ -0,0 +1,111 @@ +/*Copyright and Author + * + *This program is free software, distributed under the terms of the GNU General Public License Version 2. + * + * \author Leena , (SPAN Infotech India Pvt. Ltd., Bangalore) + * + * \module diameter + * + * \created 24\03\09 + */ + +/*! \file + \brief Connection related routines. + + */ + +#ifndef _DIAMETER_TRANSPORT_H +#define _DIAMETER_TRANSPORT_H + +#include +#include +#include + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define MAX_BUFFER_SIZE 1024 /*!< Buffer to hold the data */ +#define closesocket close + +/*!< To set SO_OOBINLINE option for the socket */ +#ifndef setoobinline +#define setoobinline(socket) { int bBool = 1; setsockopt(socket, SOL_SOCKET, SO_OOBINLINE,(const char* )&bBool,sizeof(bBool)); } +#endif + +/*!< To close the socket */ +#ifndef hardclose + #define hardclose(socket) { struct linger Linger; Linger.l_onoff = 1; Linger.l_linger = 0; setsockopt(socket,SOL_SOCKET,SO_LINGER,(const char* )&Linger,sizeof(Linger)); closesocket(socket); } +#endif + + +/*! brief Status of the transport */ +enum transport_state { + TRANSPORT_CLOSED, /*!< No connection exists */ + TRANSPORT_INIT, /*!< Transport is initialised */ + TRANSPORT_CONNECTING, /*!< When a connection attempt is being made */ + TRANSPORT_RECONNECT, /*!< When reconnection attempts are made */ + TRANSPORT_UP, /*!< When the connection is established between the peers */ + TRANSPORT_DOWN /*!< When a connection is disconnected */ +}; + +struct diameter_peer_transport { + int clientsocket; /*!< Socket for making connections */ + int serversocket; /*!< Socket for accepting the incoming connections */ + int activesocket; /*!< Socket which actively used for exchanging messages once the connection is up */ + int connected; /*!< Is high if connection exists else 0 */ + int disconnected; /*!< Is high when the diameter client is disconnected by its peer */ + enum transport_state trans_state; /*!< Holds the state of the transport */ +}; + +/*! + * \brief Creates endpoints for connection and initiates a thread for incoming packets + * + * \retval 1 on success and 0 on failure + */ +int diameter_transport_init(void); + +/*! + * \brief Sends a diameter message + * + * \param data the message to be sent + * \param data_length the length of the message to be sent + * + * \retval 1 on success and 0 on failure + */ +int diameter_transport_send(const unsigned char *data, int data_length); + +/*! + * \brief Makes a connection attempt with the diameter peer. + * + * \returns the state of the transport + */ +enum transport_state diameter_transport_connection(void); + +/*! + * \brief Closes the connection and kills the listener thread(for incoming packets) + * + */ +void diameter_transport_cleanup(void); + +/*! + * \brief Registers a timer for making connection attempts at regular intervals no connection exists. + * + * \retval 1 on success and 0 on failure + */ +int register_transporttimer(void); + +/* + * \brief Closes the existing connection and restarts the transport for a new connection. + * + * \retval 1 on success and 0 on failure + */ +int transport_restart(void); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + +#endif /* _DIAMETER_TRANSPORT_H */ +