/** * Sends a message to the peer. * \note Must be called with a lock on the peer. * @param p - peer to send to * @param msg - message to send */ void Snd_Message(peer *p, AAAMessage *msg) { AAASession *session=0; int rcode; int send_message_before_session_sm=0; touch_peer(p); if (msg->sessionId) session = get_session(msg->sessionId->data); if (session){ switch (session->type){ case AUTH_CLIENT_STATEFULL: if (is_req(msg)) auth_client_statefull_sm_process(session,AUTH_EV_SEND_REQ,msg); else { if (msg->commandCode == IMS_ASA){ if (!msg->res_code){ msg->res_code = AAAFindMatchingAVP(msg,0,AVP_Result_Code,0,0); } if (!msg->res_code) auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_UNSUCCESS,msg); else { rcode = get_4bytes(msg->res_code->data.s); if (rcode>=2000 && rcode<3000) { peer_send_msg(p,msg); send_message_before_session_sm=1; auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_SUCCESS,msg); } else auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_UNSUCCESS,msg); } }else auth_client_statefull_sm_process(session,AUTH_EV_SEND_ANS,msg); } break; case AUTH_SERVER_STATEFULL: if (is_req(msg)) { if (msg->commandCode== IMS_ASR) { auth_server_statefull_sm_process(session,AUTH_EV_SEND_ASR,msg); } else { //would be a RAR but ok! auth_server_statefull_sm_process(session,AUTH_EV_SEND_REQ,msg); } } else { if (msg->commandCode == IMS_STR) auth_server_statefull_sm_process(session,AUTH_EV_SEND_STA,msg); else auth_server_statefull_sm_process(session,AUTH_EV_SEND_ANS,msg); } break; default: break; } sessions_unlock(session->hash); } if (!send_message_before_session_sm) peer_send_msg(p,msg); }
/** * Handler for incoming Diameter requests. * @param request - the received request * @param param - generic pointer * @returns the answer to this request */ AAAMessage* callback_cdp_request(AAAMessage *request, void *param) { if (is_req(request)) { switch (request->applicationId) { case IMS_Cx: //case IMS_Dx: IMS_Cx is same as IMS_Dx 16777216 switch (request->commandCode) { case IMS_RTR: LM_INFO("Cx/Dx request handler():- Received an IMS_RTR \n"); return cxdx_process_rtr(request); break; default: LM_ERR("Cx/Dx request handler(): - Received unknown request for Cx/Dx command %d, flags %#1x endtoend %u hopbyhop %u\n", request->commandCode, request->flags, request->endtoendId, request->hopbyhopId); return 0; break; } break; default: LM_ERR("Cx/Dx request handler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode); return 0; break; } } return 0; }
/** * Handler for incoming Diameter requests. * @param request - the received request * @param param - generic pointer * @returns the answer to this request */ AAAMessage* callback_cdp_request(AAAMessage *request, void *param) { if (is_req(request)) { switch (request->applicationId) { case IMS_Rx: case IMS_Gq: switch (request->commandCode) { case IMS_RAR: LM_INFO("Rx request handler():- Received an IMS_RAR \n"); /* TODO: Add support for Re-Auth Requests */ return 0; break; case IMS_ASR: LM_INFO("Rx request handler(): - Received an IMS_ASR \n"); return rx_process_asr(request); break; default: LM_ERR("Rx request handler(): - Received unknown request for Rx/Gq command %d, flags %#1x endtoend %u hopbyhop %u\n", request->commandCode, request->flags, request->endtoendId, request->hopbyhopId); return 0; break; } break; default: LM_ERR("Rx request handler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode); return 0; break; } } return 0; }
/** * Handler for incoming Diameter requests. * @param request - the received request * @param param - generic pointer * @returns the answer to this request * but we wont/should not recieve any request here */ AAAMessage* RfRequestHandler(AAAMessage *request,void *param) { if (is_req(request)){ LOG(L_INFO,"INFO: RfRequestHandler(): We have received a request!!!\n"); #ifdef WITH_IMS_PM ims_pm_diameter_request(request); #endif switch(request->applicationId){ case IMS_Rf: switch(request->commandCode){ default : LOG(L_ERR,"ERR: RfRequestHandler(): - Received unknown request for Rf command %d\n",request->commandCode); break; } break; default: LOG(L_ERR,"ERR: RfRequestHandler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode); break; } } return 0; }
/** * This is an executor for tasks from shm. Should do the same as what is the middle of the worker_process() loop, * but this should be used when the messages will be processed by an external to cdp worker-pool. * @param ptr - the tast_t* into shm */ int worker_execute(void* ptr) { task_t *t=ptr; int r; cdp_cb_t *cb; if (!t) return -1; if (!t->msg) goto error; if (shutdownx&&(*shutdownx)) goto error; LOG(L_DBG,"Executing task for cdp: Message [%d] from Peer [%.*s]\n", t->msg->commandCode,t->p?t->p->fqdn.len:0,t->p?t->p->fqdn.s:0); r = is_req(t->msg); for(cb = callbacks->head;cb;cb = cb->next) (*(cb->cb))(t->p,t->msg,*(cb->ptr)); if (r){ AAAFreeMessage(&(t->msg)); }else{ /* will be freed by the user in upper api */ /*AAAFreeMessage(&(t.msg));*/ } shm_free(t); return -1; error: if (t->msg) AAAFreeMessage(&(t->msg)); shm_free(t); return -1; }
/** * Handler for incoming Diameter requests. * @param request - the received request * @param param - generic pointer * @returns the answer to this request */ AAAMessage* CxRequestHandler(AAAMessage *request,void *param) { if (is_req(request)){ LOG(L_INFO,"INFO:"M_NAME":CxRequestHandler(): We have received a request\n"); #ifdef WITH_IMS_PM ims_pm_diameter_request(request); #endif switch(request->applicationId){ case IMS_Cx: switch(request->commandCode){ case IMS_RTR: LOG(L_INFO,"INFO:"M_NAME":CxRequestHandler():- Received an IMS_RTR \n"); return Cx_RTA(request); break; case IMS_PPR: LOG(L_INFO,"INFO:"M_NAME":CxRequestHandler(): - Received an IMS_PPR \n"); return Cx_PPA(request); break; default : LOG(L_ERR,"ERR:"M_NAME":CxRequestHandler(): - Received unknown request for Cx command %d\n",request->commandCode); break; } break; default: LOG(L_ERR,"ERR:"M_NAME":CxRequestHandler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode); break; } } return 0; }
/** * Send a AAAMessage asynchronously. * When the response is received, the callback_f(callback_param,...) is called. * @param message - the request to be sent * @param peer_id - FQDN of the peer to send * @param callback_f - callback to be called on transactional response or transaction timeout * @param callback_param - generic parameter to call the transactional callback function with * @returns 1 on success, 0 on failure * \todo remove peer_id and add Realm routing */ AAAReturnCode AAASendMessageToPeer( AAAMessage *message, str *peer_id, AAATransactionCallback_f *callback_f, void *callback_param) { peer *p; p = get_peer_by_fqdn(peer_id); if (!p) { LOG(L_ERR,"ERROR:AAASendMessageToPeer(): Peer unknown %.*s\n",peer_id->len,peer_id->s); goto error; } if (p->state!=I_Open && p->state!=R_Open){ LOG(L_ERR,"ERROR:AAASendMessageToPeer(): Peer not connected to %.*s\n",peer_id->len,peer_id->s); goto error; } /* only add transaction following when required */ if (callback_f){ if (is_req(message)) cdp_add_trans(message,callback_f,callback_param,config->transaction_timeout,1); else LOG(L_ERR,"ERROR:AAASendMessageToPeer(): can't add transaction callback for answer.\n"); } // if (!peer_send_msg(p,message)) if (!sm_process(p,Send_Message,message,0,0)) goto error; return 1; error: AAAFreeMessage(&message); return 0; }
/** * Authorization Client State-Machine - Stateless * \Note - should be called with a lock on the session and will unlock it - do not use it after! * @param s * @param event * @param msg */ inline void auth_client_stateless_sm_process(cdp_session_t* s, int event, AAAMessage *msg) { cdp_auth_session_t *x; int rc; if (!s) return; x = &(s->u.auth); switch(x->state){ case AUTH_ST_IDLE: switch(event){ case AUTH_EV_SEND_REQ: x->state = AUTH_ST_PENDING; break; default: LOG(L_ERR,"ERR:auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n", event,auth_states[x->state]); } break; case AUTH_ST_PENDING: if (!is_req(msg)){ rc = get_result_code(msg); if (rc>=2000 && rc<3000 && get_auth_session_state(msg)==NO_STATE_MAINTAINED) event = AUTH_EV_RECV_ANS_SUCCESS; else event = AUTH_EV_RECV_ANS_UNSUCCESS; } switch(event){ case AUTH_EV_RECV_ANS_SUCCESS: x->state = AUTH_ST_OPEN; break; case AUTH_EV_RECV_ANS_UNSUCCESS: x->state = AUTH_ST_IDLE; break; default: LOG(L_ERR,"ERR:auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n", event,auth_states[x->state]); } break; case AUTH_ST_OPEN: switch(event){ case AUTH_EV_SESSION_TIMEOUT: x->state = AUTH_ST_IDLE; break; case AUTH_EV_SERVICE_TERMINATED: x->state = AUTH_ST_IDLE; break; default: LOG(L_ERR,"ERR:auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n", event,auth_states[x->state]); } break; default: LOG(L_ERR,"ERR:auth_client_stateless_sm_process(): Received event %d while in invalid state %d!\n", event,x->state); } if (s) AAASessionsUnlock(s->hash); }
/* Sets the proper result_code into the Result-Code AVP; thus avp must already * exists into the reply message */ AAAReturnCode AAASetMessageResultCode( AAAMessage *message, AAAResultCode resultCode) { if ( !is_req(message) && message->res_code) { *((unsigned int*)(message->res_code->data.s)) = htonl(resultCode); return AAA_ERR_SUCCESS; } return AAA_ERR_FAILURE; }
/** * Send a AAAMessage synchronously. * This blocks until a response is received or a transactional time-out happens. * @param message - the request to be sent * @param peer_id - FQDN of the peer to send * @returns 1 on success, 0 on failure * \todo remove peer_id and add Realm routing * \todo replace the busy-waiting lock in here with one that does not consume CPU */ AAAMessage* AAASendRecvMessageToPeer(AAAMessage *message, str *peer_id) { peer *p; gen_sem_t *sem; cdp_trans_t *t; AAAMessage *ans; p = get_peer_by_fqdn(peer_id); if (!p) { LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): Peer unknown %.*s\n",peer_id->len,peer_id->s); goto error; } if (p->state!=I_Open && p->state!=R_Open){ LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): Peer not connected to %.*s\n",peer_id->len,peer_id->s); goto error; } if (is_req(message)){ sem_new(sem,0); t = cdp_add_trans(message,sendrecv_cb,(void*)sem,config->transaction_timeout,0); // if (!peer_send_msg(p,message)) { if (!sm_process(p,Send_Message,message,0,0)){ sem_free(sem); goto error; } /* block until callback is executed */ while(sem_get(sem)<0){ if (shutdownx&&(*shutdownx)) goto error; LOG(L_WARN,"WARN:AAASendRecvMessageToPeer(): interrupted by signal or something > %s\n",strerror(errno)); } sem_free(sem); ans = t->ans; cdp_free_trans(t); return ans; } else { LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): can't add wait for answer to answer.\n"); goto error; } error: out_of_memory: AAAFreeMessage(&message); return 0; }
/** * This is the main worker process. * Takes tasks from the queue in a loop and processes them by calling the registered callbacks. * @param id - id of the worker * @returns never, exits on shutdown. */ void worker_process(int id) { task_t t; cdp_cb_t *cb; int r; LM_INFO("[%d] Worker process started...\n", id); /* init the application level for this child */ while (1) { if (shutdownx && (*shutdownx)) break; cfg_update(); t = take_task(); if (!t.msg) { if (shutdownx && (*shutdownx)) break; LM_INFO("[%d] got empty task Q(%d/%d)\n", id, tasks->start, tasks->end); continue; } LM_DBG("worker_process(): [%d] got task Q(%d/%d)\n", id, tasks->start, tasks->end); r = is_req(t.msg); for (cb = callbacks->head; cb; cb = cb->next) (*(cb->cb))(t.p, t.msg, *(cb->ptr)); if (r) { AAAFreeMessage(&(t.msg)); } else { /* will be freed by the user in upper api */ /*AAAFreeMessage(&(t.msg));*/ } } worker_poison_queue(); LM_INFO("[%d]... Worker process finished\n", id); #ifdef CDP_FOR_SER #else #ifdef PKG_MALLOC LM_DBG("Worker[%d] Memory status (pkg):\n", id); //pkg_status(); #ifdef pkg_sums pkg_sums(); #endif #endif dp_del_pid(getpid()); #endif exit(0); }
/** * Handler for incoming Diameter requests. * @param request - the received request * @param param - generic pointer * @returns the answer to this request */ AAAMessage* e2RequestHandler(AAAMessage *request,void *param) { if (is_req(request)){ LOG(L_INFO,"INFO:"M_NAME":e2RequestHandler(): We have received a request\n"); switch(request->applicationId){ case IMS_e2: switch(request->commandCode){ default : LOG(L_ERR,"ERR:"M_NAME":e2RequestHandler(): - Received unknown request for e2 command %d\n",request->commandCode); break; } break; default: LOG(L_ERR,"ERR:"M_NAME":e2RequestHandler(): - Received unknown request for app %d command %d\n", request->applicationId, request->commandCode); break; } } return 0; }
void ServerConnection::receive() { int res = tcp_recv_msg(conn.dia_conn, &conn.rb, 0, CONN_WAIT_USECS); if (res < 0) { if (res == AAA_CONN_SHUTDOWN) { INFO( M_NAME "receive(): shutdown - closing connection.\n"); closeConnection(true); } else { closeConnection(); ERROR( M_NAME "receive(): tcp_recv_reply() failed.\n"); } return; } if (!res) // nothing received return; /* obtain the structure corresponding to the message */ AAAMessage* msg = AAATranslateMessage(conn.rb.buf, conn.rb.buf_len, 0); if(!msg) { ERROR( M_NAME "receive(): message structure not obtained from message.\n"); closeConnection(); return; } #ifdef EXTRA_DEBUG AAAPrintMessage(msg); #endif if (is_req(msg)) handleRequest(msg); else handleReply(msg); AAAFreeMessage(&msg); }
/** * Receives a message and does basic processing or call the sm_process(). * This gets called from the do_receive() for every message that is received. * Basic processing, before the state machine, is done here. * @param msg - the message received * @param sp - the serviced peer that it was receiver on */ void receive_message(AAAMessage *msg,serviced_peer_t *sp) { AAA_AVP *avp1,*avp2; LM_DBG("receive_message(): [%.*s] Recv msg %d\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0, msg->commandCode); if (!sp->p){ switch (msg->commandCode){ case Code_CE: if (is_req(msg)){ avp1 = AAAFindMatchingAVP(msg,msg->avpList.head,AVP_Origin_Host,0,0); avp2 = AAAFindMatchingAVP(msg,msg->avpList.head,AVP_Origin_Realm,0,0); if (avp1&&avp2){ sp->p = get_peer_from_fqdn(avp1->data,avp2->data); } if (!sp->p) { LM_ERR("receive_msg(): Received CER from unknown peer (accept unknown=%d) -ignored\n", config->accept_unknown_peers); AAAFreeMessage(&msg); }else{ LM_DBG("receive_message(): [%.*s] This receiver has no peer associated\n", sp->p?sp->p->fqdn.len:0, sp->p?sp->p->fqdn.s:0 ); //set_peer_pipe(); make_send_pipe(sp); sm_process(sp->p,R_Conn_CER,msg,0,sp->tcp_socket); } } else{ LM_ERR("receive_msg(): Received CEA from an unknown peer -ignored\n"); AAAFreeMessage(&msg); } break; default: LM_ERR("receive_msg(): Received non-CE from an unknown peer -ignored\n"); AAAFreeMessage(&msg); } }else{ touch_peer(sp->p); switch (sp->p->state){ case Wait_I_CEA: if (msg->commandCode!=Code_CE||is_req(msg)){ sm_process(sp->p,I_Rcv_Non_CEA,msg,0,sp->tcp_socket); }else sm_process(sp->p,I_Rcv_CEA,msg,0,sp->tcp_socket); break; case I_Open: switch (msg->commandCode){ case Code_CE: if (is_req(msg)) sm_process(sp->p,I_Rcv_CER,msg,0,sp->tcp_socket); else sm_process(sp->p,I_Rcv_CEA,msg,0,sp->tcp_socket); break; case Code_DW: if (is_req(msg)) sm_process(sp->p,I_Rcv_DWR,msg,0,sp->tcp_socket); else sm_process(sp->p,I_Rcv_DWA,msg,0,sp->tcp_socket); break; case Code_DP: if (is_req(msg)) sm_process(sp->p,I_Rcv_DPR,msg,0,sp->tcp_socket); else sm_process(sp->p,I_Rcv_DPA,msg,0,sp->tcp_socket); break; default: sm_process(sp->p,I_Rcv_Message,msg,0,sp->tcp_socket); } break; case R_Open: switch (msg->commandCode){ case Code_CE: if (is_req(msg)) sm_process(sp->p,R_Rcv_CER,msg,0,sp->tcp_socket); else sm_process(sp->p,R_Rcv_CEA,msg,0,sp->tcp_socket); break; case Code_DW: if (is_req(msg)) sm_process(sp->p,R_Rcv_DWR,msg,0,sp->tcp_socket); else sm_process(sp->p,R_Rcv_DWA,msg,0,sp->tcp_socket); break; case Code_DP: if (is_req(msg)) sm_process(sp->p,R_Rcv_DPR,msg,0,sp->tcp_socket); else sm_process(sp->p,R_Rcv_DPA,msg,0,sp->tcp_socket); break; default: sm_process(sp->p,R_Rcv_Message,msg,0,sp->tcp_socket); } break; default: LM_ERR("receive_msg(): [%.*s] Received msg while peer in state %d -ignored\n", sp->p->fqdn.len, sp->p->fqdn.s, sp->p->state); AAAFreeMessage(&msg); } } }
/** * Receives a mesasge and does basic processing or call the sm_process(). * This gets called from the receive_loop for every message that is received. * @param msg - the message received * @param sock - socket received on */ void receive_message(AAAMessage *msg,int sock) { AAA_AVP *avp1,*avp2; LOG(L_DBG,"DBG:receive_message(): [%d] Recv msg %d\n",sock,msg->commandCode); if (!this_peer) { this_peer = get_peer_from_sock(sock); set_peer_pipe(); } if (!this_peer){ switch (msg->commandCode){ case Code_CE: if (is_req(msg)){ avp1 = AAAFindMatchingAVP(msg,msg->avpList.head,AVP_Origin_Host,0,0); avp2 = AAAFindMatchingAVP(msg,msg->avpList.head,AVP_Origin_Realm,0,0); if (avp1&&avp2){ this_peer = get_peer_from_fqdn(avp1->data,avp2->data); } if (!this_peer) { LOG(L_ERR,"ERROR:receive_msg(): Received CER from unknown peer (accept unknown=%d) -ignored\n", config->accept_unknown_peers); AAAFreeMessage(&msg); }else{ set_peer_pipe(); sm_process(this_peer,R_Conn_CER,msg,0,sock); } } else{ LOG(L_ERR,"ERROR:receive_msg(): Received CEA from an unknown peer -ignored\n"); AAAFreeMessage(&msg); } break; default: LOG(L_ERR,"ERROR:receive_msg(): Received non-CE from an unknown peer -ignored\n"); AAAFreeMessage(&msg); } }else{ touch_peer(this_peer); switch (this_peer->state){ case Wait_I_CEA: if (msg->commandCode!=Code_CE||is_req(msg)){ sm_process(this_peer,I_Rcv_Non_CEA,msg,0,sock); }else sm_process(this_peer,I_Rcv_CEA,msg,0,sock); break; case I_Open: switch (msg->commandCode){ case Code_CE: if (is_req(msg)) sm_process(this_peer,I_Rcv_CER,msg,0,sock); else sm_process(this_peer,I_Rcv_CEA,msg,0,sock); break; case Code_DW: if (is_req(msg)) sm_process(this_peer,I_Rcv_DWR,msg,0,sock); else sm_process(this_peer,I_Rcv_DWA,msg,0,sock); break; case Code_DP: if (is_req(msg)) sm_process(this_peer,I_Rcv_DPR,msg,0,sock); else sm_process(this_peer,I_Rcv_DPA,msg,0,sock); break; default: sm_process(this_peer,I_Rcv_Message,msg,0,sock); } break; case R_Open: switch (msg->commandCode){ case Code_CE: if (is_req(msg)) sm_process(this_peer,R_Rcv_CER,msg,0,sock); else sm_process(this_peer,R_Rcv_CEA,msg,0,sock); break; case Code_DW: if (is_req(msg)) sm_process(this_peer,R_Rcv_DWR,msg,0,sock); else sm_process(this_peer,R_Rcv_DWA,msg,0,sock); break; case Code_DP: if (is_req(msg)) sm_process(this_peer,R_Rcv_DPR,msg,0,sock); else sm_process(this_peer,R_Rcv_DPA,msg,0,sock); break; default: sm_process(this_peer,R_Rcv_Message,msg,0,sock); } break; default: LOG(L_ERR,"ERROR:receive_msg(): Received msg while peer in state %d -ignored\n",this_peer->state); AAAFreeMessage(&msg); } } }
/** * Processes an incoming message. * This actually just puts the message into a message queue. One worker will pick-it-up * and do the actual processing. * \note Must be called with a lock on the peer. * @param p - peer received from * @param msg - the message received */ void Rcv_Process(peer *p, AAAMessage *msg) { AAASession *session=0; unsigned int hash; // we need this here because after the sm_processing , we might end up // with no session any more if (msg->sessionId) session = get_session(msg->sessionId->data); if (session){ hash=session->hash; switch (session->type){ case AUTH_CLIENT_STATEFULL: if (is_req(msg)){ if (msg->commandCode==IMS_ASR) auth_client_statefull_sm_process(session,AUTH_EV_RECV_ASR,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); }else { if (msg->commandCode==IMS_STA) auth_client_statefull_sm_process(session,AUTH_EV_RECV_STA,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); } break; case AUTH_SERVER_STATEFULL: if (is_req(msg)) { auth_server_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); }else{ if (msg->commandCode==IMS_ASA) auth_server_statefull_sm_process(session,AUTH_EV_RECV_ASA,msg); else auth_server_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); } break; default: break; } sessions_unlock(hash); }else{ if (msg->sessionId){ if (msg->commandCode == IMS_ASR) auth_client_statefull_sm_process(0,AUTH_EV_RECV_ASR,msg); if (msg->commandCode == IMS_AAR) { session=AAACreateAuthSession(0,0,1,0,0); shm_str_dup(session->id,msg->sessionId->data); auth_server_statefull_sm_process(0,AUTH_EV_RECV_REQ,msg); } // Any other cases to think about? } } if (!put_task(p,msg)){ LOG(L_ERR,"ERROR:Rcv_Process(): Queue refused task\n"); AAAFreeMessage(&msg); } LOG(L_DBG,"DBG:Rcv_Process(): task added to queue\n"); // AAAPrintMessage(msg); }
/** * stateful client state machine * \Note - should be called with a lock on the session and will unlock it - do not use it after! * @param cc_acc - AAACCAccSession which uses this state machine * @param ev - Event * @param msg - AAAMessage * @returns 0 if msg should be given to the upper layer 1 if not */ inline int cc_acc_client_stateful_sm_process(cdp_session_t* s, int event, AAAMessage* msg) { cdp_cc_acc_session_t* x; int ret = 0; int rc; //return code for responses int record_type; x = &(s->u.cc_acc); LM_DBG("cc_acc_client_stateful_sm_process: processing CC App in state [%d] and event [%d]\n", x->state, event); //first run session callbacks if (s->cb) (s->cb)(event, s); LM_DBG("finished callback of event %i\n", event); switch (x->state) { case ACC_CC_ST_IDLE: switch (event) { case ACC_CC_EV_SEND_REQ: //were sending a message - CCR //assert this is an initial request. we can't move from IDLE with anything else record_type = get_accounting_record_type(msg); switch (record_type) { case 2 /*START RECORD*/: LM_DBG("sending CCR START record on session\n"); s->application_id = msg->applicationId; s->u.cc_acc.state = ACC_CC_ST_PENDING_I; //update our reservation and its timers... if they exist in CCR update_gsu_request_timers(x, msg); break; default: LM_ERR("Sending CCR with no/incorrect accounting record type AVP. In state IDLE\n"); break; } break; default: LM_ERR("Recevied unknown event [%d] in state [%d]\n", event, x->state); break; } break; case ACC_CC_ST_OPEN: switch (event) { case ACC_CC_EV_SEND_REQ: //were sending a message - CCR //make sure it is either an update or a termination. record_type = get_accounting_record_type(msg); switch (record_type) { case 3 /*UPDATE RECORD*/: LM_DBG("sending CCR UPDATE record on session\n"); s->u.cc_acc.state = ACC_CC_ST_PENDING_U; //update our reservation and its timers... update_gsu_request_timers(x, msg); break; case 4: /*TERMINATE RECORD*/ LM_DBG("sending CCR TERMINATE record on session\n"); s->u.cc_acc.state = ACC_CC_ST_PENDING_T; //update our reservation and its timers... update_gsu_request_timers(x, msg); break; default: LM_ERR("asked to send CCR with no/incorrect accounting record type AVP. In state IDLE\n"); break; } break; case ACC_CC_EV_RSVN_WARNING: //nothing we can do here, we have sent callback, client needs to send CCR Update LM_DBG("Reservation close to expiring\n"); break; default: LM_ERR("Received unknown event [%d] in state [%d]\n", event, x->state); break; } break; case ACC_CC_ST_PENDING_I: if (event == ACC_CC_EV_RECV_ANS && msg && !is_req(msg)) { rc = get_result_code(msg); if (rc >= 2000 && rc < 3000) { event = ACC_CC_EV_RECV_ANS_SUCCESS; } else { event = ACC_CC_EV_RECV_ANS_UNSUCCESS; } } switch (event) { case ACC_CC_EV_RECV_ANS_SUCCESS: x->state = ACC_CC_ST_OPEN; LM_DBG("received success response for CCR START\n"); update_gsu_response_timers(x, msg); break; case ACC_CC_EV_RECV_ANS_UNSUCCESS: //TODO: grant/terminate service callbacks to callback clients LM_ERR("failed answer on CCR START\n"); x->state = ACC_CC_ST_DISCON; break; default: LM_ERR("Received unknown event [%d] in state [%d]\n", event, x->state); break; } break; case ACC_CC_ST_PENDING_T: if (event == ACC_CC_EV_RECV_ANS && msg && !is_req(msg)) { rc = get_result_code(msg); if (rc >= 2000 && rc < 3000) { event = ACC_CC_EV_RECV_ANS_SUCCESS; } else { event = ACC_CC_EV_RECV_ANS_UNSUCCESS; } } switch (event) { case ACC_CC_EV_RECV_ANS_SUCCESS: x->state = ACC_CC_ST_DISCON; // update_gsu_response_timers(x, msg); case ACC_CC_EV_RECV_ANS_UNSUCCESS: x->state = ACC_CC_ST_DISCON; default: LM_DBG("Received event [%d] in state [%d] - cleaning up session regardless\n", event, x->state); //have to leave session alone because our client app still has to be given this msg x->discon_time = time(0); // if (msg) AAAFreeMessage(&msg); // cdp_session_cleanup(s, NULL); // s = 0; } break; case ACC_CC_ST_PENDING_U: if (event == ACC_CC_EV_RECV_ANS && msg && !is_req(msg)) { rc = get_result_code(msg); if (rc >= 2000 && rc < 3000) { event = ACC_CC_EV_RECV_ANS_SUCCESS; } else { event = ACC_CC_EV_RECV_ANS_UNSUCCESS; } } switch (event) { case ACC_CC_EV_RECV_ANS_SUCCESS: x->state = ACC_CC_ST_OPEN; LM_DBG("success CCA for UPDATE\n"); update_gsu_response_timers(x, msg); break; case ACC_CC_EV_RECV_ANS_UNSUCCESS: //TODO: check whether we grant or terminate service to callback clients x->state = ACC_CC_ST_DISCON; LM_ERR("update failed... going back to IDLE/DISCON\n"); break; default: LM_ERR("Received unknown event [%d] in state [%d]\n", event, x->state); break; } break; case ACC_CC_ST_DISCON: switch (event) { case ACC_CC_EV_SESSION_STALE: LM_DBG("stale session about to be cleared\n"); cdp_session_cleanup(s, msg); s = 0; break; default: LM_ERR("Received unknown event [%d] in state [%d]\n", event, x->state); break; } break; } if (s) { AAASessionsUnlock(s->hash); } return ret; }
/** * Processes an incoming message. * This actually just puts the message into a message queue. One worker will pick-it-up * and do the actual processing. * \note Must be called with a lock on the peer. * @param p - peer received from * @param msg - the message received */ void Rcv_Process(peer *p, AAAMessage *msg) { AAASession *session=0; int nput=0; if (msg->sessionId) session = cdp_get_session(msg->sessionId->data); if (session){ switch (session->type){ case AUTH_CLIENT_STATEFULL: if (is_req(msg)){ if (msg->commandCode==IMS_ASR) auth_client_statefull_sm_process(session,AUTH_EV_RECV_ASR,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); session = 0; }else { if (msg->commandCode==IMS_STA) nput=auth_client_statefull_sm_process(session,AUTH_EV_RECV_STA,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); session = 0; } break; case AUTH_SERVER_STATEFULL: if (is_req(msg)) { if (msg->commandCode==IMS_STR) { auth_server_statefull_sm_process(session,AUTH_EV_RECV_STR,msg); } else { auth_server_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); } session = 0; }else{ if (msg->commandCode==IMS_ASA) auth_server_statefull_sm_process(session,AUTH_EV_RECV_ASA,msg); else auth_server_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); session = 0; } break; default: AAASessionsUnlock(session->hash); session =0; break; } }else{ if (msg->sessionId){ if (msg->commandCode == IMS_ASR) auth_client_statefull_sm_process(0,AUTH_EV_RECV_ASR,msg); } } if (!nput && !put_task(p,msg)){ LM_ERR("Rcv_Process(): Queue refused task\n"); if (msg) AAAFreeMessage(&msg); } //if (msg) LM_ERR("Rcv_Process(): task added to queue command %d, flags %#1x endtoend %u hopbyhop %u\n",msg->commandCode,msg->flags,msg->endtoendId,msg->hopbyhopId); // AAAPrintMessage(msg); }
/** * stateful client state machine * \Note - should be called with a lock on the session and will unlock it - do not use it after! * @param auth - AAAAuthSession which uses this state machine * @param ev - Event * @param msg - AAAMessage * @returns 0 if msg should be given to the upper layer 1 if not */ inline int auth_client_statefull_sm_process(cdp_session_t* s, int event, AAAMessage* msg) { cdp_auth_session_t *x; int rc; int rv = 0; //return value if (!s) { switch (event) { case AUTH_EV_RECV_ASR: Send_ASA(0, msg); break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d with no session!\n", event); } return rv; } x = &(s->u.auth); if (s->cb) (s->cb)(event, s); LM_INFO("after callback of event %i\n", event); //if (x && x->state && msg) LM_ERR("auth_client_statefull_sm_process [event %i] [state %i] endtoend %u hopbyhop %u\n",event,x->state,msg->endtoendId,msg->hopbyhopId); switch (x->state) { case AUTH_ST_IDLE: switch (event) { case AUTH_EV_SEND_REQ: s->application_id = msg->applicationId; s->u.auth.state = AUTH_ST_PENDING; update_auth_session_timers(x, msg); add_auth_session_timers(x, msg); //Richard add this add: add destination realm to cdp session //use msg origin realm as the destination realm //we do this here as this is were the state changes to open //Where must we free this? s->dest_realm.s = (char*) shm_malloc(msg->dest_realm->data.len); memcpy(s->dest_realm.s, msg->dest_realm->data.s, msg->dest_realm->data.len); s->dest_realm.len = msg->dest_realm->data.len; //LM_INFO("state machine: i was in idle and i am going to pending\n"); break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!(data %p)\n", event, auth_states[x->state], x->generic_data); } break; case AUTH_ST_PENDING: if (event == AUTH_EV_RECV_ANS && msg && !is_req(msg)) { rc = get_result_code(msg); if (rc >= 2000 && rc < 3000 && get_auth_session_state(msg) == STATE_MAINTAINED) event = AUTH_EV_RECV_ANS_SUCCESS; else event = AUTH_EV_RECV_ANS_UNSUCCESS; } switch (event) { case AUTH_EV_RECV_ANS_SUCCESS: x->state = AUTH_ST_OPEN; update_auth_session_timers(x, msg); //LM_INFO("state machine: i was in pending and i am going to open\n"); break; case AUTH_EV_RECV_ANS_UNSUCCESS: LM_DBG("In state AUTH_ST_PENDING and received AUTH_EV_RECV_ANS_UNSUCCESS - nothing to do but clean up session\n"); case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SERVICE_TERMINATED: case AUTH_EV_SESSION_GRACE_TIMEOUT: cdp_session_cleanup(s, NULL); s = 0; break; default: LM_ERR("auth_client_stateless_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; case AUTH_ST_OPEN: if (event == AUTH_EV_RECV_ANS && msg && !is_req(msg)) { rc = get_result_code(msg); if (rc >= 2000 && rc < 3000 && get_auth_session_state(msg) == STATE_MAINTAINED) event = AUTH_EV_RECV_ANS_SUCCESS; else event = AUTH_EV_RECV_ANS_UNSUCCESS; } switch (event) { case AUTH_EV_SEND_REQ: // if the request is STR i should move to Discon .. // this is not in the state machine but I (Alberto Diez) need it if (msg->commandCode == IMS_STR) s->u.auth.state = AUTH_ST_DISCON; else { s->u.auth.state = AUTH_ST_OPEN; add_auth_session_timers(x, msg); } break; case AUTH_EV_RECV_ANS_SUCCESS: x->state = AUTH_ST_OPEN; update_auth_session_timers(x, msg); //LM_INFO("state machine: i was in open and i am going to open\n"); break; case AUTH_EV_RECV_ANS_UNSUCCESS: x->state = AUTH_ST_DISCON; //LM_INFO("state machine: i was in open and i am going to discon\n"); break; case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SERVICE_TERMINATED: case AUTH_EV_SESSION_GRACE_TIMEOUT: x->state = AUTH_ST_DISCON; //LM_INFO("state machine: i was in open and i am going to discon\n"); Send_STR(s, msg); break; case AUTH_EV_SEND_ASA_SUCCESS: x->state = AUTH_ST_DISCON; //LM_INFO("state machine: i was in open and i am going to discon\n"); Send_STR(s, msg); break; case AUTH_EV_SEND_ASA_UNSUCCESS: x->state = AUTH_ST_OPEN; update_auth_session_timers(x, msg); //LM_INFO("state machine: i was in open and i am going to open\n"); break; case AUTH_EV_RECV_ASR: // two cases , client will comply or will not // our client is very nice and always complys.. because // our brain is in the PCRF... if he says to do this , we do it // Alberto Diez , (again this is not Diameter RFC) x->state = AUTH_ST_DISCON; Send_ASA(s, msg); Send_STR(s, msg); break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; case AUTH_ST_DISCON: switch (event) { case AUTH_EV_RECV_ASR: x->state = AUTH_ST_DISCON; //LM_INFO("state machine: i was in discon and i am going to discon\n"); Send_ASA(s, msg); break; // Just added this because it might happen if the other peer doesnt // send a valid STA, then the session stays open forever... // We dont accept that... we have lifetime+grace_period for that // This is not in the Diameter RFC ... case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SESSION_GRACE_TIMEOUT: // thats the addition case AUTH_EV_RECV_STA: x->state = AUTH_ST_IDLE; LM_INFO("state machine: AUTH_EV_RECV_STA about to clean up\n"); if (msg) AAAFreeMessage(&msg); // if might be needed in frequency // If I register a ResponseHandler then i Free the STA there not here.. // but i dont have interest in that now.. cdp_session_cleanup(s, NULL); s = 0; rv = 1; break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; default: LM_ERR("auth_client_statefull_sm_process(): Received event %d while in invalid state %d!\n", event, x->state); } if (s) { if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s); AAASessionsUnlock(s->hash); } return rv; }
/** * Processes an incoming message. * This actually just puts the message into a message queue. One worker will pick-it-up * and do the actual processing. * \note Must be called with a lock on the peer. * @param p - peer received from * @param msg - the message received */ void Rcv_Process(peer *p, AAAMessage *msg) { AAASession *session=0; int nput=0; if (msg->sessionId) session = cdp_get_session(msg->sessionId->data); if (session){ switch (session->type){ case ACCT_CC_CLIENT: if (is_req(msg)){ LM_WARN("unhandled receive request on Credit Control Acct session\n"); AAASessionsUnlock(session->hash); //must be called because we dont call state machine here session = 0; //we dont call SM here so we mustnt set to 0 } else { cc_acc_client_stateful_sm_process(session, ACC_CC_EV_RECV_ANS, msg); session = 0; } break; case AUTH_CLIENT_STATEFULL: if (is_req(msg)){ if (msg->commandCode==IMS_ASR) auth_client_statefull_sm_process(session,AUTH_EV_RECV_ASR,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); session = 0; }else { if (msg->commandCode==IMS_STA) nput=auth_client_statefull_sm_process(session,AUTH_EV_RECV_STA,msg); else auth_client_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); session = 0; } break; case AUTH_SERVER_STATEFULL: if (is_req(msg)) { if (msg->commandCode==IMS_STR) { auth_server_statefull_sm_process(session,AUTH_EV_RECV_STR,msg); } else { auth_server_statefull_sm_process(session,AUTH_EV_RECV_REQ,msg); } session = 0; }else{ if (msg->commandCode==IMS_ASA) auth_server_statefull_sm_process(session,AUTH_EV_RECV_ASA,msg); else auth_server_statefull_sm_process(session,AUTH_EV_RECV_ANS,msg); session = 0; } break; default: AAASessionsUnlock(session->hash); session =0; break; } }else{ if (msg->sessionId){ if (msg->commandCode == IMS_ASR) auth_client_statefull_sm_process(0,AUTH_EV_RECV_ASR,msg); } } if (!nput && !put_task(p,msg)){ LM_ERR("Rcv_Process(): Queue refused task\n"); if (msg) AAAFreeMessage(&msg); } //if (msg) LM_ERR("Rcv_Process(): task added to queue command %d, flags %#1x endtoend %u hopbyhop %u\n",msg->commandCode,msg->flags,msg->endtoendId,msg->hopbyhopId); // AAAPrintMessage(msg); }
/** * Authorization Server State-Machine - Statefull * \Note - should be called with a lock on the session and will unlock it - do not use it after! * @param s * @param event * @param msg */ inline void auth_server_statefull_sm_process(cdp_session_t* s, int event, AAAMessage* msg) { cdp_auth_session_t *x; if (!s) return; x = &(s->u.auth); if (s->cb) (s->cb)(event, s); LM_DBG("after callback for event %i\n", event); switch (x->state) { case AUTH_ST_IDLE: switch (event) { case AUTH_EV_RECV_STR: break; case AUTH_EV_RECV_REQ: // The RequestHandler will generate a Send event for the answer // and we will only then now if the user is authorised or not // if its not authorised it will move back to idle and cleanup the session // so no big deal // but this is not the Diameter RFC... x->state = AUTH_ST_OPEN; // execute the cb here because we won't have a chance later if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s); // Don't unlock the session hash table because the session is returned to the user // This can only be called from the AAACreateServerAuthSession()! s = 0; break; case AUTH_EV_SEND_STA: x->state = AUTH_ST_IDLE; cdp_session_cleanup(s, msg); s = 0; break; /* Just in case we have some lost sessions */ case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SESSION_GRACE_TIMEOUT: cdp_session_cleanup(s, msg); s = 0; break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; case AUTH_ST_OPEN: if (event == AUTH_EV_SEND_ANS && msg && !is_req(msg)) { int rc = get_result_code(msg); if (rc >= 2000 && rc < 3000) event = AUTH_EV_SEND_ANS_SUCCESS; else event = AUTH_EV_SEND_ANS_UNSUCCESS; } switch (event) { case AUTH_EV_RECV_STR: break; case AUTH_EV_SEND_ANS_SUCCESS: x->state = AUTH_ST_OPEN; update_auth_session_timers(x, msg); add_auth_session_timers(x, msg); break; case AUTH_EV_SEND_ANS_UNSUCCESS: x->state = AUTH_ST_IDLE; cdp_session_cleanup(s, msg); s = 0; break; case AUTH_EV_SEND_ASR: x->state = AUTH_ST_DISCON; break; case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SESSION_GRACE_TIMEOUT: x->state = AUTH_ST_IDLE; LM_DBG("before session cleanup\n"); cdp_session_cleanup(s, msg); s = 0; break; case AUTH_EV_SEND_STA: LM_ERR("SENDING STA!!!\n"); x->state = AUTH_ST_IDLE; cdp_session_cleanup(s, msg); s = 0; break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; case AUTH_ST_DISCON: switch (event) { case AUTH_EV_RECV_STR: break; case AUTH_EV_RECV_ASA: case AUTH_EV_RECV_ASA_SUCCESS: x->state = AUTH_ST_IDLE; //cdp_session_cleanup(s,msg); break; case AUTH_EV_RECV_ASA_UNSUCCESS: Send_ASR(s, msg); // how many times will this be done? x->state = AUTH_ST_DISCON; break; case AUTH_EV_SEND_STA: x->state = AUTH_ST_IDLE; cdp_session_cleanup(s, msg); s = 0; break; default: LM_ERR("auth_client_statefull_sm_process(): Received invalid event %d while in state %s!\n", event, auth_states[x->state]); } break; default: LM_ERR("auth_client_statefull_sm_process(): Received event %d while in invalid state %d!\n", event, x->state); } if (s) { if (s->cb) (s->cb)(AUTH_EV_SESSION_MODIFIED, s); AAASessionsUnlock(s->hash); } }
/** * Sends a message to the peer. * \note Must be called with a lock on the peer. * @param p - peer to send to * @param msg - message to send */ void Snd_Message(peer *p, AAAMessage *msg) { AAASession *session=0; int rcode; int send_message_before_session_sm=0; LM_DBG("Snd_Message called to peer [%.*s] for %s with code %d \n", p->fqdn.len,p->fqdn.s,is_req(msg)?"request":"response",msg->commandCode); touch_peer(p); if (msg->sessionId) session = cdp_get_session(msg->sessionId->data); if (session){ LM_DBG("There is a session of type %d\n",session->type); switch (session->type){ case AUTH_CLIENT_STATEFULL: if (is_req(msg)) { auth_client_statefull_sm_process(session,AUTH_EV_SEND_REQ,msg); session = 0; } else { if (msg->commandCode == IMS_ASA){ if (!msg->res_code){ msg->res_code = AAAFindMatchingAVP(msg,0,AVP_Result_Code,0,0); } if (!msg->res_code) { auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_UNSUCCESS,msg); session = 0; } else { rcode = get_4bytes(msg->res_code->data.s); if (rcode>=2000 && rcode<3000) { peer_send_msg(p,msg); send_message_before_session_sm=1; auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_SUCCESS,msg); session = 0; } else { auth_client_statefull_sm_process(session,AUTH_EV_SEND_ASA_UNSUCCESS,msg); session = 0; } } }else { auth_client_statefull_sm_process(session,AUTH_EV_SEND_ANS,msg); session = 0; } } break; case AUTH_SERVER_STATEFULL: LM_DBG("this message is matched here to see what request or reply it is\n"); if (is_req(msg)) { if (msg->commandCode== IMS_ASR) { LM_DBG("ASR\n"); auth_server_statefull_sm_process(session,AUTH_EV_SEND_ASR,msg); session = 0; } else { //would be a RAR but ok! LM_DBG("other request\n"); auth_server_statefull_sm_process(session,AUTH_EV_SEND_REQ,msg); session = 0; } } else { if (msg->commandCode == IMS_STR) { LM_DBG("STA\n"); auth_server_statefull_sm_process(session,AUTH_EV_SEND_STA,msg); session = 0; } else { LM_DBG("other reply\n"); auth_server_statefull_sm_process(session,AUTH_EV_SEND_ANS,msg); session = 0; } } break; default: break; } if (session) AAASessionsUnlock(session->hash); } if (!send_message_before_session_sm) peer_send_msg(p,msg); }