From: Vasu Dasari <vdasari@gmail.com>
[mpls-ldp-portable.git] / ldp / ldp_state_machine.c
blobcc80096d0f28a48330f0f44569f123df22e99b6b
2 /*
3 * Copyright (C) James R. Leu 2000
4 * jleu@mindspring.com
6 * This software is covered under the LGPL, for more
7 * info check out http://www.gnu.org/copyleft/lgpl.html
8 */
10 #include "ldp_struct.h"
11 #include "ldp_global.h"
12 #include "ldp_session.h"
13 #include "ldp_entity.h"
14 #include "ldp_peer.h"
15 #include "ldp_if.h"
16 #include "ldp_adj.h"
17 #include "ldp_mesg.h"
18 #include "ldp_buf.h"
19 #include "ldp_state_machine.h"
21 #include "mpls_assert.h"
22 #include "mpls_socket_impl.h"
23 #include "mpls_lock_impl.h"
24 #include "mpls_trace_impl.h"
26 /* HELLO CONNECT INIT KEEP ADDR LABEL NOTIF CLOSE HTIMER KTIMER */
27 /* SES_NONE new ignore ignore ignore ignore ignore ignore close ignore ignore */
28 /* SES_NON_EXISTENT maint connect close close close close close close close ignore */
29 /* SES_INITIALIZED maint close recv_init close close close notif close close ignore */
30 /* SES_OPENSENT maint close recv_init close close close notif close close ignore */
31 /* SES_OPENREC maint close close finish close close notif close close close */
32 /* SES_OPERATIONAL maint close kmaint kmaint process process notif close close close */
34 int ldp_state_table[LDP_STATE_NUM][LDP_EVENT_NUM] = {
35 {0, 6, 6, 6, 6, 6, 6, 7, 6, 6},
36 {1, 3, 7, 7, 7, 7, 7, 7, 7, 6},
37 {1, 7, 2, 7, 7, 7, 9, 7, 7, 6},
38 {1, 7, 2, 7, 7, 7, 9, 7, 7, 6},
39 {1, 7, 7, 4, 7, 7, 9, 7, 7, 7},
40 {1, 7, 8, 8, 5, 5, 9, 7, 7, 7}};
42 mpls_return_enum ldp_buf_process(ldp_global * g, mpls_socket_handle socket,
43 ldp_buf * buf, void *extra, ldp_event_enum event, mpls_dest * from,
44 mpls_bool * more);
46 mpls_return_enum(*ldp_state_func[LDP_FUNC_NUM]) (ldp_global *, ldp_session *,
47 ldp_adj *, ldp_entity *, uint32_t, ldp_mesg *, mpls_dest *) = {
48 ldp_state_new_adjacency, /* 0 */
49 ldp_state_maintainance, /* 1 */
50 ldp_state_recv_init, /* 2 */
51 ldp_state_connect, /* 3 */
52 ldp_state_finish_init, /* 4 */
53 ldp_state_process, /* 5 */
54 ldp_state_ignore, /* 6 */
55 ldp_state_close, /* 7 */
56 ldp_state_keepalive_maintainance, /* 8 */
57 ldp_state_notif /* 9 */
60 #define LDP_FUNC_CLOSE 7
62 mpls_return_enum ldp_event(mpls_cfg_handle handle, mpls_socket_handle socket,
63 void *extra, ldp_event_enum event)
65 mpls_return_enum retval = MPLS_SUCCESS;
66 ldp_global *g = (ldp_global*)handle;
68 mpls_socket_handle socket_new = (mpls_socket_handle)0;
69 ldp_session *session = NULL;
70 ldp_entity *entity = NULL;
71 ldp_adj *adj = NULL;
73 uint8_t buffer[MPLS_PDUMAXLEN];
74 mpls_dest from;
75 ldp_mesg mesg;
76 ldp_buf buf;
78 LDP_ENTER(g->user_data, "ldp_event");
80 mpls_lock_get(g->global_lock);
82 switch (event) {
83 case LDP_EVENT_TCP_DATA:
84 case LDP_EVENT_UDP_DATA:
86 mpls_bool more;
88 buf.current = buffer;
89 buf.buffer = buffer;
90 buf.total = MPLS_PDUMAXLEN;
91 buf.size = 0;
92 buf.current_size = 0;
93 buf.want = 0;
95 /* do this so a failure will know which session caused it */
96 if (event == LDP_EVENT_TCP_DATA) {
97 session = extra;
100 do {
101 retval = ldp_buf_process(g, socket, &buf, extra, event, &from, &more);
102 } while (retval == MPLS_SUCCESS && more == MPLS_BOOL_TRUE);
103 break;
105 case LDP_EVENT_TCP_LISTEN:
107 socket_new = mpls_socket_tcp_accept(g->socket_handle, socket, &from);
109 if (mpls_socket_handle_verify(g->socket_handle, socket_new) ==
110 MPLS_BOOL_FALSE) {
111 LDP_PRINT(g->user_data, "Failed accepting socket\n");
112 retval = MPLS_FAILURE;
113 } else if (!(session = ldp_session_create_passive(g, socket_new,
114 &from))) {
115 mpls_socket_close(g->socket_handle, socket_new);
116 LDP_PRINT(g->user_data, "Failure creating passive session\n");
117 retval = MPLS_FATAL;
118 } else {
119 retval = ldp_state_machine(g, session, NULL, NULL,
120 LDP_EVENT_CONNECT, &mesg, &from);
122 break;
124 case LDP_EVENT_TCP_CONNECT:
126 retval = mpls_socket_connect_status(g->socket_handle, socket);
127 session = (ldp_session *)extra;
129 if (retval == MPLS_SUCCESS) {
130 /* only get this case if we did a non-block connect */
131 mpls_socket_writelist_del(g->socket_handle, socket);
133 /* Fill the "from" address */
134 mpls_socket_get_remote_name(g->socket_handle, socket, &from);
135 retval = ldp_state_machine(g, session, NULL, NULL,
136 LDP_EVENT_CONNECT, &mesg, &from);
137 } else if (retval != MPLS_NON_BLOCKING) {
138 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,
139 "ldp_event: LDP_EVENT_TCP_CONNECT errno = %d\n",
140 mpls_socket_get_errno(g->socket_handle, socket));
141 } else {
142 /* non-blocking connect is still blocking, we'll try again in a bit */
143 retval = MPLS_SUCCESS;
145 break;
147 case LDP_EVENT_CLOSE:
149 retval = ldp_state_machine(g, session, adj, entity,
150 LDP_EVENT_CLOSE, &mesg, &from);
151 break;
153 default:
155 MPLS_ASSERT(0);
159 /* ldp_state_machine return MPLS_SUCCESS when it has handled the event
160 to completion. If the handling off the event results in the session
161 needing to be shutdown MPLS_FAILURE is returned. If the handling of
162 the event requires the LDP be shutdown LD_FATAL is returned, and
163 passed back to the user. other values are invalid */
165 switch (retval) {
166 case MPLS_FAILURE:
168 /* if shutting down the session results in LDP_FATAL, then pass it
169 * back to the user */
171 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,
172 "ldp_event: FAILURE executing a CLOSE\n");
174 retval = ldp_state_machine(g, session, adj, entity, LDP_EVENT_CLOSE,
175 NULL, &from);
177 if (retval == MPLS_FATAL) {
178 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,
179 "ldp_event: CLOSE failed: FATAL propogated to the environemnt\n");
181 break;
183 case MPLS_FATAL:
185 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,
186 "ldp_event: FATAL propogated to the environemnt\n");
187 break;
189 case MPLS_SUCCESS:
191 break;
193 default:
195 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_ERROR,
196 "ldp_event: invalid return value of %d\n", retval);
197 break;
201 mpls_lock_release(g->global_lock);
203 LDP_EXIT(g->user_data, "ldp_event");
205 return retval;
208 mpls_return_enum ldp_state_machine(ldp_global * g, ldp_session * session,
209 ldp_adj * adj, ldp_entity * entity, uint32_t event, ldp_mesg * msg,
210 mpls_dest * from)
212 int state = LDP_STATE_NONE;
213 int func = 0;
214 mpls_return_enum retval = MPLS_FAILURE;
216 LDP_ENTER(g->user_data, "ldp_state_machine");
218 if (session) {
219 state = session->state;
220 } else if (adj) {
221 state = LDP_STATE_NON_EXIST;
224 if (state >= LDP_STATE_NONE && state <= LDP_STATE_OPERATIONAL) {
225 if (event <= LDP_EVENT_KTIMER) {
226 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL, LDP_TRACE_FLAG_STATE,
227 "FSM: state %d, event %d\n", state, event);
228 func = ldp_state_table[state][event];
229 retval = ldp_state_func[func] (g, session, adj, entity, event, msg, from);
233 LDP_EXIT(g->user_data, "ldp_state_machine");
234 return retval;
237 mpls_return_enum ldp_buf_process(ldp_global * g, mpls_socket_handle socket,
238 ldp_buf * buf, void *extra, ldp_event_enum event, mpls_dest * from,
239 mpls_bool * more)
242 mpls_return_enum retval = MPLS_SUCCESS;
243 ldp_session *session = NULL;
244 ldp_entity *entity = NULL;
245 ldp_adj *adj = NULL;
246 ldp_mesg mesg;
248 int size = 0;
250 LDP_ENTER(g->user_data, "ldp_buf_process");
252 *more = MPLS_BOOL_TRUE;
254 memset(&mesg, 0, sizeof(mesg));
255 if (!buf->want) {
256 buf->want = MPLS_LDP_HDRSIZE;
259 read_again:
261 switch (event) {
262 case LDP_EVENT_TCP_DATA:
264 session = (ldp_session *) extra;
265 MPLS_ASSERT(session);
266 session->mesg_rx++;
268 size = mpls_socket_tcp_read(g->socket_handle, socket,
269 buf->buffer + buf->size, buf->want - buf->size);
271 if (!size) {
272 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL,
273 LDP_TRACE_FLAG_ERROR, "ldp_event: LDP_EVENT_TCP_DATA errno = %d\n",
274 mpls_socket_get_errno(g->socket_handle, socket));
276 retval = MPLS_FAILURE;
277 session->shutdown_notif = LDP_NOTIF_SHUTDOWN;
278 session->shutdown_fatal = MPLS_BOOL_TRUE;
279 goto ldp_event_end;
282 if (size < 0) {
283 retval = MPLS_SUCCESS;
284 *more = MPLS_BOOL_FALSE;
285 goto ldp_event_end;
287 break;
289 case LDP_EVENT_UDP_DATA:
291 size = mpls_socket_udp_recvfrom(g->socket_handle, socket,
292 buf->buffer + buf->size, buf->total - buf->size, from);
294 if (!size) {
295 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL,
296 LDP_TRACE_FLAG_ERROR, "ldp_event: LDP_EVENT_UDP_DATA errno = %d\n",
297 mpls_socket_get_errno(g->socket_handle, socket));
298 retval = MPLS_FAILURE;
299 goto ldp_event_end;
302 if (size < 0) {
303 retval = MPLS_SUCCESS;
304 *more = MPLS_BOOL_FALSE;
305 goto ldp_event_end;
307 break;
309 default:
311 MPLS_ASSERT(0);
312 break;
316 buf->current_size += size;
317 buf->size += size;
319 decode_again:
321 if (buf->size < buf->want) {
322 retval = MPLS_SUCCESS;
323 *more = MPLS_BOOL_FALSE;
324 goto ldp_event_end;
327 /* upon succesful decode the pduLength will be non 0 */
328 if (!mesg.header.pduLength) {
329 if (ldp_decode_header(g, buf, &mesg) != MPLS_SUCCESS) {
330 retval = MPLS_FAILURE;
332 if (session) {
333 session->shutdown_notif = LDP_NOTIF_BAD_MESG_LEN;
335 goto ldp_event_end;
338 /* -buf->size is already 10 (the size of the full header
339 * -pduLength include 6 bytes of the header
341 * therefore add 4 so we can compare buf->want to buf->size and
342 * not have to adjust
344 buf->want = mesg.header.pduLength + 4;
345 if (buf->size < buf->want) {
346 goto read_again;
348 if (buf->size > buf->want) {
349 buf->current_size = buf->want - MPLS_LDP_HDRSIZE;
353 do {
354 if (ldp_decode_one_mesg(g, buf, &mesg) != MPLS_SUCCESS) {
355 retval = MPLS_FAILURE;
357 if (session) {
358 session->shutdown_notif = LDP_NOTIF_BAD_MESG_LEN;
360 goto ldp_event_end_loop;
363 switch (ldp_mesg_get_type(&mesg)) {
364 case MPLS_HELLO_MSGTYPE:
366 mpls_oper_state_enum oper_state = MPLS_OPER_DOWN;
367 mpls_inet_addr addr;
368 int labelspace = 0;
369 int targeted;
371 event = LDP_EVENT_HELLO;
373 targeted = 0;
374 ldp_mesg_hello_get_targeted(&mesg, &targeted);
375 ldp_mesg_hdr_get_lsraddr(&mesg, &addr);
376 ldp_mesg_hdr_get_labelspace(&mesg, &labelspace);
378 if (targeted) {
379 ldp_peer *peer = NULL;
380 if ((peer = ldp_global_find_peer_addr(g, &addr))) {
381 entity = ldp_peer_get_entity(peer);
382 oper_state = peer->oper_state;
384 } else {
385 ldp_if *iff = NULL;
386 if ((iff = ldp_global_find_if_handle(g, from->if_handle))) {
387 entity = ldp_if_get_entity(iff);
388 oper_state = iff->oper_state;
392 if (!entity) {
393 /* No entity! No choice but to ignore this packet */
394 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL,
395 LDP_TRACE_FLAG_NORMAL, "ldp_event: unknown entity\n");
396 goto ldp_event_end_loop;
397 } else if (entity->admin_state == MPLS_ADMIN_DISABLE) {
398 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL,
399 LDP_TRACE_FLAG_NORMAL, "ldp_event: entity is disabled\n");
400 goto ldp_event_end_loop;
401 } else if (oper_state == MPLS_OPER_DOWN) {
402 LDP_TRACE_LOG(g->user_data, MPLS_TRACE_STATE_ALL,
403 LDP_TRACE_FLAG_NORMAL, "ldp_event: entity is down\n");
404 goto ldp_event_end_loop;
408 if ((adj = ldp_entity_find_adj(entity, &mesg))) {
409 session = adj->session;
410 } else {
411 session = NULL;
413 /* if we don't have an adj one will be create by state machine */
414 break;
416 case MPLS_INIT_MSGTYPE:
418 event = LDP_EVENT_INIT;
419 break;
421 case MPLS_NOT_MSGTYPE:
423 event = LDP_EVENT_NOTIF;
424 break;
426 case MPLS_KEEPAL_MSGTYPE:
428 event = LDP_EVENT_KEEP;
429 break;
431 case MPLS_LBLWITH_MSGTYPE:
432 case MPLS_LBLREL_MSGTYPE:
433 case MPLS_LBLREQ_MSGTYPE:
434 case MPLS_LBLMAP_MSGTYPE:
435 case MPLS_LBLABORT_MSGTYPE:
437 event = LDP_EVENT_LABEL;
438 break;
440 case MPLS_ADDR_MSGTYPE:
441 case MPLS_ADDRWITH_MSGTYPE:
443 event = LDP_EVENT_ADDR;
444 break;
446 default:
448 MPLS_ASSERT(0);
452 retval =
453 ldp_state_machine(g, session, adj, entity, event, &mesg, from);
455 ldp_event_end_loop:
457 if (retval != MPLS_SUCCESS) {
458 break;
460 } while ((buf->current_size > 0) && (*more == MPLS_BOOL_TRUE));
462 if (buf->want < buf->size) {
463 buf->current_size = buf->size - buf->want;
464 buf->size = buf->current_size;
465 memmove(buf->buffer, buf->current, buf->current_size);
466 } else {
467 buf->size = 0;
470 buf->current = buf->buffer;
471 memset(&mesg, 0, sizeof(mesg));
472 buf->want = MPLS_LDP_HDRSIZE;
474 if (buf->current_size) {
475 goto decode_again;
478 ldp_event_end:
480 LDP_EXIT(g->user_data, "ldp_buf_process");
482 return retval;