/** * Diameter base protocol state-machine processing. * This function get's called for every event. It updates the states and can trigger * other events. * @param p - the peer for which the event happened * @param event - the event that happened * @param msg - if a Diameter message was received this is it, or NULL if not * @param peer_locked - if the peer lock is already aquired * @param sock - socket that this event happened on, or NULL if unrelated * @returns 1 on success, 0 on error. Also the peer states are updated */ int sm_process(peer *p,peer_event_t event,AAAMessage *msg,int peer_locked,int sock) { int result_code; peer_event_t next_event; int msg_received=0; if (!peer_locked) lock_get(p->lock); LM_DBG("sm_process(): Peer %.*s \tState %s \tEvent %s\n", p->fqdn.len,p->fqdn.s,dp_states[p->state],dp_events[event-101]); switch (p->state){ case Closed: switch (event){ case Start: p->state = Wait_Conn_Ack; next_event = I_Snd_Conn_Req(p); if (next_event==I_Rcv_Conn_NAck) sm_process(p,next_event,0,1,p->I_sock); else{ /* wait for fd to be transmitted to the respective receiver, in order to get a send pipe opened */ } break; case R_Conn_CER: R_Accept(p,sock); result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); msg=0; if (result_code>=2000 && result_code<3000) p->state = R_Open; else { R_Disc(p); p->state = Closed; } log_peer_list(L_INFO); break; case Stop: /* just ignore this state */ p->state = Closed; break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_Conn_Ack: switch(event){ case I_Rcv_Conn_Ack: I_Snd_CER(p); p->state = Wait_I_CEA; break; case I_Rcv_Conn_NAck: Cleanup(p,p->I_sock); p->state = Closed; break; case R_Conn_CER: if (p->r_cer) AAAFreeMessage(&(p->r_cer)); R_Accept(p,sock); result_code = Process_CER(p,msg); if (result_code>=2000 && result_code<3000){ p->state = Wait_Conn_Ack_Elect; p->r_cer = msg; } else { p->state = Closed; AAAFreeMessage(&msg); R_Disc(p); I_Disc(p); } break; case Timeout: Error(p,p->I_sock); p->state = Closed; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_I_CEA: switch(event){ case I_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { Cleanup(p,p->I_sock); p->state = Closed; } log_peer_list(L_INFO); break; case R_Conn_CER: if (p->r_cer) AAAFreeMessage(&(p->r_cer)); R_Accept(p,sock); result_code = Process_CER(p,msg); if (result_code>=2000 && result_code<3000){ p->state = Wait_Returns; if (Elect(p,msg)){ // won the election = > I_Disc(), R_Send_CEA() LM_INFO("sm_process():Wait_I_CEA Win Elect \n"); sm_process(p,Win_Election,msg,1,sock); } else { // lost the election => wait for I_Recv_CEA, then R_Disc() LM_INFO("sm_process():Wait_I_CEA Lose Elect \n"); p->r_cer = msg; sm_process(p,I_Peer_Disc,0,1,p->I_sock); } } else{ Snd_CEA(p,msg,result_code,p->R_sock); R_Disc(p); I_Disc(p); p->state=Closed; break; } break; case I_Peer_Disc: I_Disc(p); p->state = Closed; break; case I_Rcv_Non_CEA: Error(p,p->I_sock); p->state = Closed; break; case Timeout: Error(p,p->I_sock); p->state = Closed; break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_Conn_Ack_Elect: switch(event){ case I_Rcv_Conn_Ack: I_Snd_CER(p); if (p->r_cer){ p->state = Wait_Returns; if (Elect(p,p->r_cer)){ // won the election = > I_Disc(), R_Send_CEA() LM_INFO("sm_process():Wait_Conn_Ack_Elect Win Elect \n"); sm_process(p,Win_Election,p->r_cer,1,sock); p->r_cer = 0; } else { // lost the election => wait for I_Recv_CEA, then R_Disc() LM_INFO("sm_process():Wait_Conn_Ack_Elect Lose Elect \n"); AAAFreeMessage(&p->r_cer); } } else { LM_ERR("sm_process():Wait_Conn_Ack_Elect, I_Rcv_Conn_Ack, No R-CER ! \n"); p->state = Wait_I_CEA; } break; case I_Rcv_Conn_NAck: Cleanup(p,p->I_sock); if (p->r_cer){ result_code = Process_CER(p,p->r_cer); Snd_CEA(p,p->r_cer,result_code,p->R_sock); p->r_cer=0; if (result_code>=2000 && result_code<3000) p->state = R_Open; else { R_Disc(p); p->state = Closed; // p->state = R_Open; /* Or maybe I should disconnect it?*/ } }else{ LM_ERR("sm_process():Wait_Conn_Ack_Elect, I_Rcv_Conn_NAck No R-CER ! \n"); } break; case R_Peer_Disc: R_Disc(p); p->state = Wait_Conn_Ack; break; case R_Conn_CER: R_Reject(p,sock); AAAFreeMessage(&msg); p->state = Wait_Conn_Ack_Elect; break; case Timeout: if (p->I_sock>=0) Error(p,p->I_sock); if (p->R_sock>=0) Error(p,p->R_sock); p->state = Closed; break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_Returns: switch(event){ case Win_Election: /* this is the Win Election -> I is dropped, R is kept */ LM_INFO("sm_process():Wait_Returns Win Elect \n"); I_Disc(p); result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000){ p->state = R_Open; }else{ R_Disc(p); p->state = Closed; } break; case I_Peer_Disc: I_Disc(p); if (p->r_cer){ result_code = Process_CER(p,p->r_cer); Snd_CEA(p,p->r_cer,result_code,p->R_sock); p->r_cer=0; if (result_code>=2000 && result_code<3000){ p->state = R_Open; }else{ R_Disc(p); p->state = Closed; } }else { LM_ERR("sm_process():Wait_Returns, I_Peer_Disc No R-CER ! \n"); } break; case I_Rcv_CEA: /* this is the Lost Election -> I is kept, R dropped */ LM_INFO("sm_process():Wait_Returns Lost Elect \n"); R_Disc(p); result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { Cleanup(p,p->I_sock); p->state = Closed; } break; case R_Peer_Disc: R_Disc(p); p->state = Wait_I_CEA; break; case R_Conn_CER: R_Reject(p,p->R_sock); AAAFreeMessage(&msg); p->state = Wait_Returns; break; case Timeout: if (p->I_sock>=0) Error(p,p->I_sock); if (p->R_sock>=0) Error(p,p->R_sock); p->state = Closed; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case R_Open: switch (event){ case Send_Message: Snd_Message(p,msg); p->state = R_Open; break; case R_Rcv_Message: // delayed processing until out of the critical zone //Rcv_Process(p,msg); msg_received = 1; p->state = R_Open; break; case R_Rcv_DWR: result_code = Process_DWR(p,msg); Snd_DWA(p,msg,result_code,p->R_sock); p->state = R_Open; break; case R_Rcv_DWA: Process_DWA(p,msg); p->state = R_Open; break; case R_Conn_CER: R_Reject(p,sock); AAAFreeMessage(&msg); p->state = R_Open; break; case Stop: Snd_DPR(p); p->state = Closing; break; case R_Rcv_DPR: Snd_DPA(p,msg,AAA_SUCCESS,p->R_sock); R_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case R_Peer_Disc: R_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case R_Rcv_CER: result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000) p->state = R_Open; else { /*R_Disc(p);p.state = Closed;*/ p->state = R_Open; /* Or maybe I should disconnect it?*/ } break; case R_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = R_Open; else { /*R_Disc(p);p.state = Closed;*/ p->state = R_Open; /* Or maybe I should disconnect it?*/ } log_peer_list(L_INFO); break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case I_Open: switch (event){ case Send_Message: Snd_Message(p,msg); p->state = I_Open; break; case I_Rcv_Message: // delayed processing until out of the critical zone //Rcv_Process(p,msg); msg_received = 1; p->state = I_Open; break; case I_Rcv_DWR: result_code = Process_DWR(p,msg); Snd_DWA(p,msg,result_code,p->I_sock); p->state =I_Open; break; case I_Rcv_DWA: Process_DWA(p,msg); p->state =I_Open; break; case R_Conn_CER: R_Reject(p,sock); AAAFreeMessage(&msg); p->state = I_Open; break; case Stop: Snd_DPR(p); p->state = Closing; break; case I_Rcv_DPR: Snd_DPA(p,msg,2001,p->I_sock); I_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case I_Peer_Disc: I_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case I_Rcv_CER: result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->I_sock); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { /*I_Disc(p);p.state = Closed;*/ p->state = I_Open; /* Or maybe I should disconnect it?*/ } break; case I_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { /*I_Disc(p);p.state = Closed;*/ p->state = I_Open; /* Or maybe I should disconnect it?*/ } break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Closing: switch(event){ case I_Rcv_DPA: I_Disc(p); p->state = Closed; break; case R_Rcv_DPA: R_Disc(p); p->state = Closed; break; case Timeout: if (p->I_sock>=0) Error(p,p->I_sock); if (p->R_sock>=0) Error(p,p->R_sock); p->state = Closed; break; case I_Peer_Disc: I_Disc(p); p->state = Closed; break; case R_Peer_Disc: R_Disc(p); p->state = Closed; break; default: LM_ERR("sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; } if (!peer_locked) lock_release(p->lock); if (msg_received) Rcv_Process(p,msg); return 1; error: if (!peer_locked) lock_release(p->lock); return 0; }
/** * Diameter base protocol state-machine processing. * This function get's called for every event. It updates the states and can trigger * other events. * @param p - the peer for which the event happened * @param event - the event that happened * @param msg - if a Diameter message was received this is it, or NULL if not * @param peer_locked - if the peer lock is already aquired * @param sock - socket that this event happened on, or NULL if unrelated * @returns 1 on success, 0 on error. Also the peer states are updated */ int sm_process(peer *p,peer_event_t event,AAAMessage *msg,int peer_locked,int sock) { int result_code; peer_event_t next_event; int msg_received=0; if (!peer_locked) lock_get(p->lock); LOG(L_INFO,"DBG:sm_process(): Peer %.*s \tState %s \tEvent %s\n", p->fqdn.len,p->fqdn.s,dp_states[p->state],dp_events[event-101]); switch (p->state){ case Closed: switch (event){ case Start: p->state = Wait_Conn_Ack; next_event = I_Snd_Conn_Req(p); sm_process(p,next_event,0,1,p->I_sock); break; case R_Conn_CER: R_Accept(p,sock); result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000) p->state = R_Open; else { R_Disc(p); p->state = Closed; } log_peer_list(L_INFO); break; case Stop: /* just ignore this state */ p->state = Closed; break; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_Conn_Ack: switch(event){ case I_Rcv_Conn_Ack: I_Snd_CER(p); p->state = Wait_I_CEA; break; case I_Rcv_Conn_NAck: Cleanup(p,p->I_sock); p->state = Closed; break; /* Commented as not reachable*/ case R_Conn_CER: R_Accept(p,sock); result_code = Process_CER(p,msg); if (result_code>=2000 && result_code<3000) p->state = Wait_Conn_Ack_Elect; else { p->state = Wait_Conn_Ack; close(sock); } break; case Timeout: Error(p,p->I_sock); p->state = Closed; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_I_CEA: switch(event){ case I_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { Cleanup(p,p->I_sock); p->state = Closed; } log_peer_list(L_INFO); break; case R_Conn_CER: R_Accept(p,sock); result_code = Process_CER(p,msg); p->state = Wait_Returns; if (Elect(p,msg)) sm_process(p,Win_Election,msg,1,sock); break; case I_Peer_Disc: I_Disc(p); p->state = Closed; break; case I_Rcv_Non_CEA: Error(p,p->I_sock); p->state = Closed; break; case Timeout: Error(p,p->I_sock); p->state = Closed; break; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; /* commented as not reachable */ case Wait_Conn_Ack_Elect: switch(event){ default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Wait_Returns: switch(event){ case Win_Election: I_Disc(p); result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000){ p->state = R_Open; }else{ R_Disc(p); p->state = Closed; } break; case I_Peer_Disc: I_Disc(p); result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000){ p->state = R_Open; }else{ R_Disc(p); p->state = Closed; } break; case I_Rcv_CEA: R_Disc(p); result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { Cleanup(p,p->I_sock); p->state = Closed; } break; case R_Peer_Disc: R_Disc(p); p->state = Wait_I_CEA; break; case R_Conn_CER: R_Reject(p,p->R_sock); p->state = Wait_Returns; break; case Timeout: if (p->I_sock>=0) Error(p,p->I_sock); if (p->R_sock>=0) Error(p,p->R_sock); p->state = Closed; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case R_Open: switch (event){ case Send_Message: Snd_Message(p,msg); p->state = R_Open; break; case R_Rcv_Message: // delayed processing until out of the critical zone //Rcv_Process(p,msg); msg_received = 1; p->state = R_Open; break; case R_Rcv_DWR: result_code = Process_DWR(p,msg); Snd_DWA(p,msg,result_code,p->R_sock); p->state = R_Open; break; case R_Rcv_DWA: Process_DWA(p,msg); p->state = R_Open; break; case R_Conn_CER: R_Reject(p,sock); p->state = R_Open; break; case Stop: Snd_DPR(p); p->state = Closing; break; case R_Rcv_DPR: Snd_DPA(p,msg,AAA_SUCCESS,p->R_sock); R_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case R_Peer_Disc: R_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case R_Rcv_CER: result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->R_sock); if (result_code>=2000 && result_code<3000) p->state = R_Open; else { /*R_Disc(p);p.state = Closed;*/ p->state = R_Open; /* Or maybe I should disconnect it?*/ } break; case R_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = R_Open; else { /*R_Disc(p);p.state = Closed;*/ p->state = R_Open; /* Or maybe I should disconnect it?*/ } log_peer_list(L_INFO); break; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case I_Open: switch (event){ case Send_Message: Snd_Message(p,msg); p->state = I_Open; break; case I_Rcv_Message: // delayed processing until out of the critical zone //Rcv_Process(p,msg); msg_received = 1; p->state = I_Open; break; case I_Rcv_DWR: result_code = Process_DWR(p,msg); Snd_DWA(p,msg,result_code,p->I_sock); p->state =I_Open; break; case I_Rcv_DWA: Process_DWA(p,msg); p->state =I_Open; break; case R_Conn_CER: R_Reject(p,sock); p->state = I_Open; break; case Stop: Snd_DPR(p); p->state = Closing; break; case I_Rcv_DPR: Snd_DPA(p,msg,2001,p->I_sock); R_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case I_Peer_Disc: I_Disc(p); p->state = Closed; log_peer_list(L_INFO); break; case I_Rcv_CER: result_code = Process_CER(p,msg); Snd_CEA(p,msg,result_code,p->I_sock); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { /*I_Disc(p);p.state = Closed;*/ p->state = I_Open; /* Or maybe I should disconnect it?*/ } break; case I_Rcv_CEA: result_code = Process_CEA(p,msg); if (result_code>=2000 && result_code<3000) p->state = I_Open; else { /*I_Disc(p);p.state = Closed;*/ p->state = I_Open; /* Or maybe I should disconnect it?*/ } break; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; case Closing: switch(event){ case I_Rcv_DPA: I_Disc(p); p->state = Closed; break; case R_Rcv_DPA: R_Disc(p); p->state = Closed; break; case Timeout: if (p->I_sock>=0) Error(p,p->I_sock); if (p->R_sock>=0) Error(p,p->R_sock); p->state = Closed; break; case I_Peer_Disc: I_Disc(p); p->state = Closed; break; case R_Peer_Disc: R_Disc(p); p->state = Closed; break; default: LOG(L_DBG,"DBG:sm_process(): In state %s invalid event %s\n", dp_states[p->state],dp_events[event-101]); goto error; } break; } if (!peer_locked) lock_release(p->lock); if (msg_received) Rcv_Process(p,msg); return 1; error: if (!peer_locked) lock_release(p->lock); return 0; }