/** 
 * 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);
}
Example #2
0
/**
 * Looks for a CC Acc dession with a given id and returns it if found
 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
 * @returns the new AAASession or null on error
 */
AAASession* AAAGetCCAccSession(str id)
{
	AAASession *x=cdp_get_session(id);
	if (x){
		switch (x->type){
			case ACCT_CC_CLIENT:
				return x;
			default:
				AAASessionsUnlock(x->hash);
				return 0;
		}
	}
	return 0;
}
Example #3
0
/**
 * Looks for an Auth session with a given id and returns it if found
 * \note Returns with a lock on AAASession->hash. Unlock when done working with the result
 * @returns the new AAASession or null on error
 */
AAASession* AAAGetAuthSession(str id)
{
	AAASession *x=cdp_get_session(id);
	if (x){
		switch (x->type){
			case AUTH_CLIENT_STATEFULL:
			case AUTH_CLIENT_STATELESS:
			case AUTH_SERVER_STATEFULL:
			case AUTH_SERVER_STATELESS:
				return x;
			default:
				AAASessionsUnlock(x->hash);
				return 0;
		}
	}
	return 0;
}
Example #4
0
/**
 * Finds a session in the session hash table.
 * \note Returns with a lock on the sessions[x->hash].lock!!!
 * @param id - the id of the session
 * @returns the session if found or 0 if not
 */
cdp_session_t* cdp_get_session(str id)
{
	unsigned int hash;
	cdp_session_t *x;
	if (!id.len) return 0;
	hash = get_str_hash(id,sessions_hash_size);
	LM_DBG("called get session with id %.*s and hash %u\n",id.len,id.s,hash);
	AAASessionsLock(hash);
		for(x = sessions[hash].head;x;x=x->next){
			LM_DBG("looking for |%.*s| in |%.*s|\n",id.len,id.s,x->id.len,x->id.s);
			if (x->id.len == id.len &&
				strncasecmp(x->id.s,id.s,id.len)==0)
					return x;
		}
	AAASessionsUnlock(hash);
	LM_DBG("no session found\n");
	return 0;
}
Example #5
0
void cdp_sessions_log()
{
	int hash;
	cdp_session_t *x;

	LM_DBG(ANSI_MAGENTA"------- CDP Sessions ----------------\n"ANSI_GREEN);
	for(hash=0;hash<sessions_hash_size;hash++){
		AAASessionsLock(hash);
		for(x = sessions[hash].head;x;x=x->next) {
			LM_DBG(ANSI_GRAY" %3u. [%.*s] AppId [%d] Type [%d]\n",
					hash,
					x->id.len,x->id.s,
					x->application_id,
					x->type);
			switch (x->type){
				case AUTH_CLIENT_STATEFULL:
				case AUTH_SERVER_STATEFULL:
					LM_DBG(ANSI_GRAY"\tAuth State [%d] Timeout [%d] Lifetime [%d] Grace [%d] Generic [%p]\n",
							x->u.auth.state,
							(int)(x->u.auth.timeout-time(0)),
							x->u.auth.lifetime?(int)(x->u.auth.lifetime-time(0)):-1,
							(int)(x->u.auth.grace_period),
							x->u.auth.generic_data);
					break;
				case ACCT_CC_CLIENT:
					LM_DBG(ANSI_GRAY"\tCCAcct State [%d] Charging Active [%c (%d)s] Reserved Units(valid=%ds) [%d] Generic [%p]\n",
							x->u.cc_acc.state,
							(x->u.cc_acc.charging_start_time&&x->u.cc_acc.state!=ACC_CC_ST_DISCON)?'Y':'N',
							x->u.cc_acc.charging_start_time?(int)((int)time(0) - (int)x->u.cc_acc.charging_start_time):-1,
							x->u.cc_acc.reserved_units?(int)((int)x->u.cc_acc.last_reservation_request_time + x->u.cc_acc.reserved_units_validity_time) - (int)time(0):-1,
							x->u.cc_acc.reserved_units,
							x->u.cc_acc.generic_data);
					break;
				default:
					break;
			}
		}
		AAASessionsUnlock(hash);
	}
	LM_DBG(ANSI_MAGENTA"-------------------------------------\n"ANSI_GREEN);
}
Example #6
0
/**
 * Removes and frees a session.
 * \note must be called with a lock on the x->hash and it will unlock on exit. Do not use x after calling this
 *
 * @param x - the session to remove
 */
void del_session(cdp_session_t *x)
{
	unsigned int hash;

	if (!x) return;

	hash = x->hash;
	if (hash < 0 || hash >= sessions_hash_size) {
		LM_ERR("del_session: x->hash :%d out of range of sessions_hash_size: %d !\n",hash, sessions_hash_size);
		return;
	}

	if (sessions[x->hash].head == x) sessions[x->hash].head = x->next;
	else if (x->prev) x->prev->next = x->next;
	if (sessions[x->hash].tail == x) sessions[x->hash].tail = x->prev;
	else if (x->next) x->next->prev = x->prev;

	AAASessionsUnlock(hash);

	free_session(x);
}
Example #7
0
/**
 * Authorization Server State-Machine - Stateless
 * \Note - should be called with a lock on the session and will unlock it - do not use it after!
 *
 * @param auth
 * @param event
 * @param msg
 */
inline void auth_server_stateless_sm_process(cdp_session_t* s, int event, AAAMessage* msg) {
    /* empty - no state change, anyway */
    /*
            cdp_auth_session_t *x;
            int rc;
            if (!s) return;
            x = &(s->u.auth);
            switch(x->state){
                    case AUTH_ST_IDLE:
                            switch(event){
                                    default:
                                            LM_ERR("auth_server_stateless_sm_process(): Received invalid event %d while in state %s!\n",
                                                    event,auth_state[x->state]);
                            }
                            break;
                    default:
                            LM_ERR("auth_server_stateless_sm_process(): Received event %d while in invalid state %d!\n",
                                    event,x->state);
            }
     */
    if (s) AAASessionsUnlock(s->hash);
}
Example #8
0
/**
 * Get the first peer that is connected from the list of routing entries.
 * @param r - the list of routing entries to look into
 * @returns - the peer or null if none connected
 */
peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, int app_id, int vendor_id) {
	peer * peers[LB_MAX_PEERS];
	int peer_count = 0;
	int prev_metric = 0;
	routing_entry *i;
	peer *p;
	int j;
	time_t least_recent_time;
	struct timespec time_spec;

	if (cdp_session) {
		/*try and find an already used peer for this session - sticky*/
		if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
			//we have an old sticky peer. let's make sure it's up and connected before we use it.
			AAASessionsUnlock(cdp_session->hash); /*V1.1 - Don't attempt to hold two locks at same time */
			p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
			AAASessionsLock(cdp_session->hash); /*V1.1 - As we were...no call seems to pass cdp_session unlocked */
			if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
				p->last_selected = time(NULL);
				LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);
				return p;
			}
		}
	}

	for (i = r; i; i = i->next) {
		if (peer_count >= LB_MAX_PEERS)
			break;
		p = get_peer_by_fqdn(&(i->fqdn));
		if (!p)
			LM_DBG("The peer %.*s does not seem to be connected or configured\n",
					i->fqdn.len, i->fqdn.s);
		else
			LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s,
					(p->state == I_Open || p->state == R_Open) ? "opened" : "closed");
		if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
			LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s);
			if (peer_count != 0) {//check the metric
				if (i->metric != prev_metric)
					break;
				//metric must be the same
				peers[peer_count++] = p;
			} else {//we're first
				prev_metric = i->metric;
				peers[peer_count++] = p;
			}
		}
	}

	if (peer_count == 0) {
		return 0;
	}

	least_recent_time = peers[0]->last_selected;
	LM_DBG("peer [%.*s] was last used @ %ld\n", peers[0]->fqdn.len, peers[0]->fqdn.s, peers[0]->last_selected);
	p = peers[0];
	for (j = 1; j < peer_count; j++) {
		LM_DBG("Peer [%.*s] was last used at [%ld]\n", peers[j]->fqdn.len, peers[j]->fqdn.s, peers[j]->last_selected);
		if (peers[j]->last_selected < least_recent_time) {
			least_recent_time = peers[j]->last_selected;
			p = peers[j];
		}
	}

	ser_clock_gettime(&time_spec);

	p->last_selected = (time_spec.tv_sec*1000000) + round(time_spec.tv_nsec / 1.0e3); // Convert nanoseconds to microseconds
	LM_DBG("chosen peer [%.*s]\n", p->fqdn.len, p->fqdn.s);

	if (cdp_session) {
		if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) {
			LM_DBG("not enough storage for sticky peer - allocating more\n");
			if (cdp_session->sticky_peer_fqdn.s)
				shm_free(cdp_session->sticky_peer_fqdn.s);

			cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1);
			if (!cdp_session->sticky_peer_fqdn.s) {
				LM_ERR("no more shm memory\n");
				return 0;
			}
			cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1;
			memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1);
		}
		cdp_session->sticky_peer_fqdn.len = p->fqdn.len;
		memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len);
	}

	return p;
}
Example #9
0
/**
 * 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);
    }
}
Example #10
0
/**
 * 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;
}
Example #11
0
/**
 * 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);
	
}
Example #12
0
/**
 * 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);
	
}
Example #13
0
/**
 * 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);

}
Example #14
0
/**
 * 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;
}
Example #15
0
int cdp_sessions_timer(time_t now, void* ptr)
{
	int hash;
	cdp_session_t *x,*n;
	for(hash=0;hash<sessions_hash_size;hash++){
		AAASessionsLock(hash);
		for(x = sessions[hash].head;x;x=n) {
			n = x->next;
			switch (x->type){
				case ACCT_CC_CLIENT:
					if (x->u.cc_acc.type == ACC_CC_TYPE_SESSION) {
						//check for old, stale sessions, we need to do something more elegant
						//here to ensure that if a CCR start record is sent and the client never sends anything
						//else that we catch it and clean up the session from within CDP, calling all callbacks, etc
						if ((time(0) > (x->u.cc_acc.discon_time + GRACE_DISCON_TIMEOUT)) && (x->u.cc_acc.state==ACC_CC_ST_DISCON)) {
							cc_acc_client_stateful_sm_process(x, ACC_CC_EV_SESSION_STALE, 0);
						}
						//check reservation timers - again here we are assuming CC-Time applications
						int last_res_timestamp = x->u.cc_acc.last_reservation_request_time;
						int res_valid_for = x->u.cc_acc.reserved_units_validity_time;
						int last_reservation = x->u.cc_acc.reserved_units;
						int buffer_time = 15; //15 seconds - TODO: add as config parameter
						//we should check for reservation expiries if the state is open
						if(x->u.cc_acc.state==ACC_CC_ST_OPEN){
						    if (last_res_timestamp) {
							    //we have obv already started reservations
							    if ((last_res_timestamp + res_valid_for) < (time(0) + last_reservation + buffer_time)) {
								    LM_DBG("reservation about to expire, sending callback\n");
								    cc_acc_client_stateful_sm_process(x, ACC_CC_EV_RSVN_WARNING, 0);
							    }

						    }
						}
						/* TODO: if reservation has expired we need to tear down the session. Ideally 
						 * the client application (module) should do this but for completeness we should
						 * put a failsafe here too.
						 */
					}
					break;
				case AUTH_CLIENT_STATEFULL:
					if (x->u.auth.timeout>=0 && x->u.auth.timeout<=now){
						//Session timeout
						LM_CRIT("session TIMEOUT\n");
						auth_client_statefull_sm_process(x,AUTH_EV_SESSION_TIMEOUT,0);
					} else if (x->u.auth.lifetime>0 && x->u.auth.lifetime+x->u.auth.grace_period<=now){
						//lifetime + grace timeout
						LM_CRIT("lifetime+grace TIMEOUT\n");
						auth_client_statefull_sm_process(x,AUTH_EV_SESSION_GRACE_TIMEOUT,0);
					}else if (x->u.auth.lifetime>0 && x->u.auth.lifetime<=now){
						//lifetime timeout
						LM_CRIT("lifetime+grace TIMEOUT\n");
						auth_client_statefull_sm_process(x,AUTH_EV_SESSION_LIFETIME_TIMEOUT,0);
					}
					break;
				case AUTH_SERVER_STATEFULL:
					if (x->u.auth.timeout>=0 && x->u.auth.timeout<=now){
						//Session timeout
						LM_CRIT("session TIMEOUT\n");
						auth_server_statefull_sm_process(x,AUTH_EV_SESSION_TIMEOUT,0);
					}else if (x->u.auth.lifetime>0 && x->u.auth.lifetime+x->u.auth.grace_period<=now){
						//lifetime + grace timeout
						LM_CRIT("lifetime+grace TIMEOUT\n");
						auth_server_statefull_sm_process(x,AUTH_EV_SESSION_GRACE_TIMEOUT,0);
					}else if (x->u.auth.lifetime>0 && x->u.auth.lifetime<=now){
						//lifetime timeout
						LM_CRIT("lifetime+grace TIMEOUT\n");
						auth_server_statefull_sm_process(x,AUTH_EV_SESSION_LIFETIME_TIMEOUT,0);
					}
					break;
				default:
					break;

			}
		}
		AAASessionsUnlock(hash);
	}
	if (now%5==0)cdp_sessions_log();
	return 1;
}