/* * Copyright (C) James R. Leu 2000 * jleu@mindspring.com * * This software is covered under the LGPL, for more * info check out http://www.gnu.org/copyleft/lgpl.html */ #include "ldp_struct.h" #include "ldp_assert.h" #include "ldp_global.h" #include "ldp_session.h" #include "ldp_entity.h" #include "ldp_peer.h" #include "ldp_if.h" #include "ldp_adj.h" #include "ldp_pdu.h" #include "ldp_msg.h" #include "ldp_state_machine.h" #include "ldp_socket_impl.h" #include "ldp_lock_impl.h" #include "ldp_trace_impl.h" /* HELLO CONNECT INIT KEEP ADDR LABEL NOTIF CLOSE HTIMER KTIMER */ /* SES_NONE new ignore ignore ignore ignore ignore ignore ignore ignore ignore */ /* SES_NON_EXISTENT maint connect close close close close close ignore close ignore */ /* SES_INITIALIZED maint close recv_init close close close notif close close ignore */ /* SES_OPENSENT maint close recv_init close close close notif close close ignore */ /* SES_OPENREC maint close close finish close close notif close close close */ /* SES_OPERATIONAL maint close kmaint kmaint process process notif close close close */ int ldp_state_table[LDP_STATE_NUM][LDP_EVENT_NUM] = {{0,6,6,6,6,6,6,6,6,6}, {1,3,7,7,7,7,7,6,7,6}, {1,7,2,7,7,7,9,7,7,6}, {1,7,2,7,7,7,9,7,7,6}, {1,7,7,4,7,7,9,7,7,7}, {1,7,8,8,5,5,9,7,7,7}}; ldp_return_enum (*ldp_state_func[LDP_FUNC_NUM])(ldp_global*,ldp_session*, ldp_adj*,ldp_entity*,uint32_t,ldp_msg*,ldp_dest*) = { ldp_state_new_adjacency, /* 0 */ ldp_state_maintainance, /* 1 */ ldp_state_recv_init, /* 2 */ ldp_state_connect, /* 3 */ ldp_state_finish_init, /* 4 */ ldp_state_process, /* 5 */ ldp_state_ignore, /* 6 */ ldp_state_close, /* 7 */ ldp_state_keepalive_maintainance, /* 8 */ ldp_state_notification}; /* 9 */ #define LDP_FUNC_CLOSE 7 ldp_return_enum ldp_event(ldp_cfg_handle g,ldp_socket_handle socket, ldp_dest *from,ldp_if_handle if_handle,ldp_pdu *pdu,void* extra, ldp_event_enum event) { ldp_entity *entity = NULL; ldp_adj *adj = NULL; ldp_msg *msg = NULL; ldp_return_enum retval = LDP_FAILURE; ldp_session *session = NULL; ldp_peer *peer = NULL; ldp_if *iff = NULL; ldp_socket_handle socket_new = (ldp_socket_handle)0; ldp_dest _from,*ifrom; ldp_inet_addr _lsr_id; int32_t data_size = 0, buf_size = 4096; uint8_t buf[4096]; int targeted; ldp_oper_state_enum oper_state; LDP_ENTER(g->user_data,"ldp_event"); ldp_lock_get(g->global_lock); if(from == NULL) ifrom = &_from; else ifrom = from; switch(event) { case LDP_EVENT_TCP_DATA: { session = (ldp_session*)extra; if(pdu == NULL) { data_size = ldp_socket_tcp_read(g->socket_handle,socket,buf,buf_size); if(data_size > 0) { pdu = ldp_pdu_create_decode(g,buf,buf_size,data_size); if(pdu == NULL) goto ldp_event_end; } else if(data_size == 0) { event = LDP_EVENT_CLOSE; } else { LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,"ldp_event: LDP_EVENT_TCP_DATA errno = %d\n", ldp_socket_get_errno(g->socket_handle,socket)); } } /* session != NULL adj == NULL ifrom == NULL entity == NULL msg != NULL */ break; } case LDP_EVENT_UDP_DATA: { if(pdu == NULL) { data_size = ldp_socket_udp_recvfrom(g->socket_handle,socket,buf, buf_size,ifrom); pdu = ldp_pdu_create_decode(g,buf,buf_size,data_size); if(pdu == NULL) goto ldp_event_end; } /* session == NULL adj == NULL ifrom != NULL entity != NULL msg != NULL */ break; } case LDP_EVENT_TCP_LISTEN: { /* yes this is supposed to be from NOT ifrom */ if(from == NULL) { socket_new = ldp_socket_tcp_accept(g->socket_handle,socket,ifrom); } else { socket_new = (ldp_socket_handle)extra; } if(ldp_socket_handle_verify(g->socket_handle,socket_new) == LDP_FALSE) { LDP_PRINT(g->user_data,"invalide socket handle\n"); perror("accept"); goto ldp_event_end; } session = ldp_session_create_passive(g,NULL,socket_new); if(session == NULL) { ldp_socket_close(g->socket_handle,socket); LDP_PRINT(g->user_data,"passive session failure\n"); goto ldp_event_end; } event = LDP_EVENT_CONNECT; /* session != NULL adj == NULL ifrom != NULL entity = NULL msg = NULL */ break; } case LDP_EVENT_TCP_CONNECT: { /* I only get this case if we did a non-block connect */ ldp_socket_writelist_del(g->socket_handle,socket); session = (ldp_session*)extra; switch(ldp_socket_connect_status(g->socket_handle,socket)) { case LDP_SUCCESS: break; default: ldp_session_shutdown(g,session,1); LDP_PRINT(g->user_data,"conect failed\n"); goto ldp_event_end; break; } /* session != NULL adj != NULL ifrom == NULL entity != NULL msg = NULL */ event = LDP_EVENT_CONNECT; break; } case LDP_EVENT_CLOSE: break; default: LDP_ASSERT(0); } /* we only have a PDU if it TCP DATA or UDP DATA */ if(pdu != NULL) { LDP_LIST_REMOVE_HEAD(&pdu->msg,msg,_pdu); while(msg != NULL) { oper_state = LDP_DOWN; switch(ldp_msg_get_type(msg)) { case MPLS_HELLO_MSGTYPE: targeted = 0; ldp_msg_hello_get_targeted(msg,&targeted); if(targeted) { if(ldp_msg_hdr_get_lsraddr(msg,&_lsr_id) == LDP_FAILURE) { LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,"ldp_event: bad mesg\n"); goto ldp_event_end_loop; } if((peer = ldp_global_find_peer_lsrid(g,&_lsr_id)) != NULL) { entity = ldp_peer_get_entity(peer); oper_state = peer->oper_state; } } else { if((iff = ldp_global_find_if_handle(g,ifrom->if_handle)) != NULL) { entity = ldp_if_get_entity(iff); oper_state = LDP_UP; } } if(entity == NULL || entity->admin_state == LDP_DISABLE || oper_state == LDP_DOWN) { LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL, LDP_TRACE_FLAG_NORMAL,"ldp_event: unknown entity\n"); /* No entity! No choice by to ignore this packet */ goto ldp_event_end_loop; } adj = ldp_entity_find_adj(entity,msg); event = LDP_EVENT_HELLO; break; case MPLS_INIT_MSGTYPE: event = LDP_EVENT_INIT; break; case MPLS_NOT_MSGTYPE: event = LDP_EVENT_NOTIF; break; case MPLS_KEEPAL_MSGTYPE: event = LDP_EVENT_KEEP; break; case MPLS_LBLWITH_MSGTYPE: case MPLS_LBLREL_MSGTYPE: case MPLS_LBLREQ_MSGTYPE: case MPLS_LBLMAP_MSGTYPE: case MPLS_LBLABORT_MSGTYPE: event = LDP_EVENT_LABEL; break; case MPLS_ADDR_MSGTYPE: case MPLS_ADDRWITH_MSGTYPE: event = LDP_EVENT_ADDR; break; default: LDP_ASSERT(0); } retval = ldp_state_machine(g,session,adj,entity,event,msg,ifrom); ldp_event_end_loop: ldp_msg_delete(msg); LDP_LIST_REMOVE_HEAD(&pdu->msg,msg,_pdu); } ldp_pdu_delete(pdu); pdu = NULL; } else { /* it can only be CONNECT or LISTEN or CLOSE */ retval = ldp_state_machine(g,session,adj,entity,event,NULL,ifrom); } ldp_event_end: if(pdu != NULL) { LDP_LIST_REMOVE_HEAD(&pdu->msg,msg,_pdu); while(msg != NULL) { ldp_msg_delete(msg); LDP_LIST_REMOVE_HEAD(&pdu->msg,msg,_pdu); } ldp_pdu_delete(pdu); } ldp_lock_release(g->global_lock); LDP_EXIT(g->user_data,"ldp_event"); return retval; } ldp_return_enum ldp_state_machine(ldp_global *g,ldp_session *session, ldp_adj *adj,ldp_entity *entity,uint32_t event,ldp_msg *msg, ldp_dest *from) { int state = LDP_STATE_NONE; int func = 0; ldp_return_enum retval = LDP_FAILURE; LDP_ENTER(g->user_data,"ldp_state_machine"); if(session) { state = session->state; } else if(adj) { state = LDP_STATE_NON_EXIST; } if(state >= LDP_STATE_NONE && state <= LDP_STATE_OPERATIONAL) { if(event <= LDP_EVENT_KTIMER) { LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_STATE, "FSM: state %d, event %d\n",state,event); func = ldp_state_table[state][event]; retval = ldp_state_func[func](g,session,adj,entity,event,msg,from); } } LDP_EXIT(g->user_data,"ldp_state_machine"); return retval; #if 0 LDP_ASSERT(0); return ldp_state_func[LDP_FUNC_CLOSE](g,session,adj,entity,event,msg,from); #endif }