/** * 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); }
/** * 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); } }
/** * 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; }
/** * 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; }
int process_incoming(peer *p,AAAMessage *msg,void* ptr) { AAAMessage *ans=0; int result; int crdtonce, crdtpsec, crdtpMb; char *user_subscriber; int acr_cid; LOG(L_INFO,"process_incoming - Received Diameter message from %s\n",msg->orig_host->data.s); switch(msg->applicationId){ case IMS_Rf: LOG(L_DBG,"process_incoming IMS_RF - Received Diameter Accounting Answer from %s\n",msg->orig_host->data.s); if(get_result_code(msg,&result)) { if(result == DIAMETER_SUCCESS) { if(!Rf_get_call_record_id(msg, &acr_cid)) return 0; if(!Rf_get_subscriber(msg, &user_subscriber)) return 0; if(!Ro_get_Credit_onceoff(msg, &crdtonce)) return 0; if(!Ro_get_Credit_persec(msg, &crdtpsec)) return 0; if(!Ro_get_Credit_perMbyte(msg, &crdtpMb)) return 0; event_handler(YES,0,acr_cid,user_subscriber,crdtonce,crdtpsec,crdtpMb); } else { LOG(L_ERR,"process_incoming IMS_RF- Diameter returned no Success on ACA"); return 0; } } else { LOG(L_ERR,"process_incoming IMS_RF - Result code not found\n"); return 0; } return 1; break; case IMS_Ro: LOG(L_DBG,"process_incoming IMS_Ro- Received Diameter Credit Control Answer from %s\n",msg->orig_host->data.s); if(get_result_code(msg,&result)) { if(result == DIAMETER_SUCCESS) { if(!Ro_get_call_record_id(msg, &acr_cid)) return 0; if(!Ro_get_subscriber(msg, &user_subscriber)) return 0; if(!Ro_get_Credit_onceoff(msg, &crdtonce)) return 0; if(!Ro_get_Credit_persec(msg, &crdtpsec)) return 0; if(!Ro_get_Credit_perMbyte(msg, &crdtpMb)) return 0; event_handler(YES,1,acr_cid,user_subscriber,crdtonce,crdtpsec,crdtpMb); } else { LOG(L_ERR,"process_incoming IMS_Ro - Diameter returned no Success on CCA"); return 0; } } else { LOG(L_ERR,"process_incoming IMS_Ro - Result code not found\n"); return 0; } return 1; break; default: LOG(L_ERR,"process_incoming IMS_Ro: Received unserviced AppID [%d]\n",msg->applicationId); ans = send_unknown_request_answer(msg); } return 1; }
/** * stateful client state machine * @param auth - AAAAuthSession which uses this state machine * @param ev - Event * @param req - AAAMessage */ void auth_sm_process_stateful(AAAAuthSession* auth, int ev, AAAMessage* req, AAAMessage* ans) { int rc; LOG(L_INFO, "in auth_sm_process_stateful\n"); switch (*auth->st) { case AUTH_ST_IDLE: LOG(L_INFO, "INF: auth_sm_process_stateful: IDLE\n"); switch (ev) { case AUTH_EV_SEND_REQ: LOG(L_INFO, "INF: send AAR\n"); *auth->st = AUTH_ST_PENDING; *ans = *AAASendRecvMessage(req, auth->fqdn); rc = get_result_code(ans); if (rc == AAA_SUCCESS) { LOG(L_INFO, "INF: receive AAA success\n"); auth_sm_process_stateful(auth, AUTH_EV_RECV_ANS_SUCCESS, NULL, NULL); } else { LOG(L_INFO, "INF: receive AAA unsuccess\n"); auth_sm_process_stateful(auth, AUTH_EV_RECV_ANS_UNSUCCESS, NULL, NULL); //AAAPrintMessage(ans); } break; } break; case AUTH_ST_PENDING: LOG(L_INFO, "INF: auth_sm_process_stateful: PENDING\n"); switch (ev) { case AUTH_EV_RECV_ANS_SUCCESS: *auth->st = AUTH_ST_OPEN; break; case AUTH_EV_RECV_ANS_UNSUCCESS: *auth->st = AUTH_ST_IDLE; LOG(L_INFO, "INF: terminate auth session\n"); AAADropAuthSession(auth); LOG(L_INFO, "active session number: %d\n", length_auth_list()); break; } break; case AUTH_ST_OPEN: LOG(L_INFO, "INF: auth_sm_process_stateful: OPEN\n"); switch (ev) { case AUTH_EV_STR: LOG(L_INFO, "send STR\n"); *auth->st = AUTH_ST_DISCON; ans = AAASendRecvMessage(req, auth->fqdn); if (ans) { LOG(L_INFO, "receive STA successful\n"); auth_sm_process_stateful(auth, AUTH_EV_STA_SUCCESS, NULL, NULL); } else { LOG(L_INFO, "receive STA unsuccessful\n"); auth_sm_process_stateful(auth, AUTH_EV_STA_UNSUCCESS, NULL, NULL); } break; } break; case AUTH_ST_DISCON: LOG(L_INFO, "INF: auth_sm_process_stateful: DISCON\n"); switch(ev) { case AUTH_EV_STA_SUCCESS: break; case AUTH_EV_STA_UNSUCCESS: *auth->st = AUTH_ST_IDLE; LOG(L_INFO, "INF: terminate auth session\n"); AAADropAuthSession(auth); LOG(L_INFO, "active session number: %d\n", length_auth_list()); break; } break; } }
TEST(RESULT, result) { RESULT_OK(); ASSERT_STREQ(get_result(), ERR_OK) << "Should be equal"; ASSERT_STREQ(get_result_code(), SUCCESS) << "Should be equal"; }