026ba0ae05b1adcf5a41f20c3a3037c42e802604
[dragonfly.git] / usr.sbin / atm / scspd / scsp_hfsm.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/usr.sbin/atm/scspd/scsp_hfsm.c,v 1.3 1999/08/28 01:15:33 peter Exp $
27  *      @(#) $DragonFly: src/usr.sbin/atm/scspd/scsp_hfsm.c,v 1.2 2003/06/17 04:29:52 dillon Exp $
28  */
29
30
31 /*
32  * Server Cache Synchronization Protocol (SCSP) Support
33  * ----------------------------------------------------
34  *
35  * HELLO finite state machine
36  *
37  */
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <netatm/queue.h> 
45 #include <netatm/atm.h>
46 #include <netatm/atm_if.h>
47 #include <netatm/atm_sap.h>
48 #include <netatm/atm_sys.h>
49 #include <netatm/atm_ioctl.h>
50   
51 #include <errno.h>
52 #include <libatm.h>
53 #include <stdio.h>
54 #include <syslog.h>
55
56 #include "scsp_msg.h"
57 #include "scsp_if.h"
58 #include "scsp_var.h"
59
60 /*
61  * HELLO FSM actions
62  */
63 #define HELLO_ACTION_CNT        7
64 int     scsp_hello_act_00 __P((Scsp_dcs *, Scsp_msg *));
65 int     scsp_hello_act_01 __P((Scsp_dcs *, Scsp_msg *));
66 int     scsp_hello_act_02 __P((Scsp_dcs *, Scsp_msg *));
67 int     scsp_hello_act_03 __P((Scsp_dcs *, Scsp_msg *));
68 int     scsp_hello_act_04 __P((Scsp_dcs *, Scsp_msg *));
69 int     scsp_hello_act_05 __P((Scsp_dcs *, Scsp_msg *));
70 int     scsp_hello_act_06 __P((Scsp_dcs *, Scsp_msg *));
71
72 static int (*scsp_action_vector[HELLO_ACTION_CNT])() = {
73         scsp_hello_act_00,
74         scsp_hello_act_01,
75         scsp_hello_act_02,
76         scsp_hello_act_03,
77         scsp_hello_act_04,
78         scsp_hello_act_05,
79         scsp_hello_act_06
80 };
81
82 /*
83  * HELLO FSM state table
84  */
85 static int hello_state_table[SCSP_HFSM_EVENT_CNT][SCSP_HFSM_STATE_CNT] = {
86         /* 0  1  2  3                */
87         {  1, 1, 1, 1 },        /* 0 */
88         {  0, 2, 2, 2 },        /* 1 */
89         {  0, 3, 3, 3 },        /* 2 */
90         {  0, 0, 4, 4 },        /* 3 */
91         {  0, 5, 5, 6 },        /* 4 */
92 };
93
94 /*
95  * HELLO finite state machine
96  *
97  * Arguments:
98  *      dcsp    pointer to a DCS control block for the neighbor
99  *      event   the event which has occurred
100  *      msg     pointer to received message, if there is one
101  *
102  * Returns:
103  *      0       success
104  *      errno   error encountered
105  *
106  */
107 int
108 scsp_hfsm(dcsp, event, msg)
109         Scsp_dcs        *dcsp;
110         int             event;
111         Scsp_msg        *msg;
112 {
113         int     action, rc, state;
114
115         /*
116          * Select an action from the state table
117          */
118         state = dcsp->sd_hello_state;
119         action = hello_state_table[event][state];
120         if (scsp_trace_mode & SCSP_TRACE_HFSM) {
121                 scsp_trace("HFSM: state=%d, event=%d, action=%d\n",
122                                 state, event, action);
123         }
124         if (action >= HELLO_ACTION_CNT || action <= 0) {
125                 scsp_log(LOG_ERR, "Hello FSM--invalid action %d; state=%d, event=%d",
126                                 action, dcsp->sd_hello_state, event);
127                 abort();
128         }
129
130         /*
131          * Perform the selected action
132          */
133         rc = scsp_action_vector[action](dcsp, msg);
134
135         return(rc);
136 }
137
138
139 /*
140  * HELLO finite state machine action 0
141  * Unexpected action -- log an error message
142  *
143  * Arguments:
144  *      dcsp    pointer to DCS control block
145  *      msg     pointer to received message (ignored)
146  *
147  * Returns:
148  *      EOPNOTSUPP      always returns EOPNOTSUPP
149  *
150  */
151 int
152 scsp_hello_act_00(dcsp, msg)
153         Scsp_dcs        *dcsp;
154         Scsp_msg        *msg;
155 {
156         scsp_log(LOG_ERR, "Hello FSM error--unexpected action, state=%d",
157                         dcsp->sd_hello_state);
158         return(EOPNOTSUPP);
159 }
160
161
162 /*
163  * HELLO finite state machine action 1
164  * VCC open -- send HELLO message, start hello timer, go to Waiting
165  * state
166  *
167  * Arguments:
168  *      dcsp    pointer to DCS control block
169  *      msg     pointer to received message (ignored)
170  *
171  * Returns:
172  *      0       success
173  *      errno   error encountered
174  *
175  */
176 int
177 scsp_hello_act_01(dcsp, msg)
178         Scsp_dcs        *dcsp;
179         Scsp_msg        *msg;
180 {
181         int             rc;
182
183         /*
184          * Cancel the VCC open timer if it's running
185          */
186         HARP_CANCEL(&dcsp->sd_open_t);
187
188         /*
189          * Go to Waiting state
190          */
191         dcsp->sd_hello_state = SCSP_HFSM_WAITING;
192
193         /*
194          * Send a Hello message
195          */
196         rc = scsp_send_hello(dcsp);
197         if (rc == 0) {
198                 /*
199                  * Success--start the Hello timer
200                  */
201                 HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
202                                 scsp_hello_timeout);
203         }
204
205         return(rc);
206 }
207
208
209 /*
210  * HELLO finite state machine action 2
211  * VCC closed -- notify CA FSM, go to Down state, try to re-open VCC
212  *
213  * Arguments:
214  *      dcsp    pointer to DCS control block
215  *      msg     pointer to received message (ignored)
216  *
217  * Returns:
218  *      0       success
219  *      errno   error encountered
220  *
221  */
222 int
223 scsp_hello_act_02(dcsp, msg)
224         Scsp_dcs        *dcsp;
225         Scsp_msg        *msg;
226 {
227         int             rc;
228
229         /*
230          * Cancel any current timers
231          */
232         HARP_CANCEL(&dcsp->sd_hello_h_t);
233         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
234
235         /*
236          * Log the loss of the VCC
237          */
238         if (dcsp->sd_hello_state > SCSP_HFSM_WAITING) {
239                 scsp_log(LOG_ERR, "VC to %s closed",
240                                 format_atm_addr(&dcsp->sd_addr));
241         }
242
243         /*
244          * Tell the CA FSM that the conection to the DCS is lost
245          */
246         rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
247
248         /*
249          * Go to Down state
250          */
251         dcsp->sd_hello_state = SCSP_HFSM_DOWN;
252
253         /*
254          * If our ID is lower than the DCS's, wait a second before
255          * trying to connect.  This should keep both of us from
256          * trying to connect at the same time, resulting in two
257          * VCCs being open.
258          */
259         if (scsp_cmp_id(&dcsp->sd_server->ss_lsid,
260                         &dcsp->sd_dcsid) < 0) {
261                 /*
262                  * Our ID is lower--start the VCC open timer for one
263                  * second so we'll try to open the VCC if the DCS
264                  * doesn't do it by then
265                  */
266                 HARP_TIMER(&dcsp->sd_open_t, 1, scsp_open_timeout);
267         } else {
268                 /*
269                  * Our ID is higher--try to reopen the VCC immediately
270                  */
271                 if (scsp_dcs_connect(dcsp)) {
272                         /*
273                          * Conncect failed -- set a timer and try
274                          * again later
275                          */
276                         HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
277                                         scsp_open_timeout);
278                 }
279         }
280
281         return(0);
282 }
283
284
285 /*
286  * HELLO finite state machine action 3
287  * Hello timer expired -- send HELLO message, restart hello timer
288  *
289  * Arguments:
290  *      dcsp    pointer to DCS control block
291  *      msg     pointer to received message (ignored)
292  *
293  * Returns:
294  *      0       success
295  *      errno   error encountered
296  *
297  */
298 int
299 scsp_hello_act_03(dcsp, msg)
300         Scsp_dcs        *dcsp;
301         Scsp_msg        *msg;
302 {
303         int             rc;
304
305         /*
306          * Send a Hello message
307          */
308         rc = scsp_send_hello(dcsp);
309         if (rc == 0) {
310                 /*
311                  * Success--restart the Hello timer
312                  */
313                 HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
314                                 scsp_hello_timeout);
315         }
316
317         return(rc);
318 }
319
320
321 /*
322  * HELLO finite state machine action 4
323  * Receive timer expired -- if we haven't received any Hellos, notify
324  * CA FSM and go to Waiting state;  if we've received Hellos, but we
325  * weren't in the receiver ID list, go to Unidirectional state
326  *
327  * Arguments:
328  *      dcsp    pointer to DCS control block
329  *      msg     pointer to received message (ignored)
330  *
331  * Returns:
332  *      0       success
333  *      errno   error encountered
334  *
335  */
336 int
337 scsp_hello_act_04(dcsp, msg)
338         Scsp_dcs        *dcsp;
339         Scsp_msg        *msg;
340 {
341         int     rc = 0;
342
343         /*
344          * Check whether we'ver received any Hellos lately
345          */
346         if (dcsp->sd_hello_rcvd) {
347                 /*
348                  * We've had Hellos since the receive timer was
349                  * started--go to Unidirectional state
350                  */
351                 dcsp->sd_hello_rcvd = 0;
352                 dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
353         } else {
354                 /*
355                  * We haven't seen any Hellos at all from the DCS in
356                  * hello_interval * dead_factor seconds--go to Waiting
357                  * state
358                  */
359                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
360         }
361
362         /*
363          * Notify the CA FSM
364          */
365         rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
366
367         return(rc);
368 }
369
370
371 /*
372  * HELLO finite state machine action 5
373  * Message received -- Ignore all but HELLO messages;  if local server
374  * is in receiver list, notify CA FSM and go to Bidirectional state;
375  * otherwise, go to Unidirectional state
376  *
377  * Arguments:
378  *      dcsp    pointer to DCS control block
379  *      msg     pointer to received message
380  *
381  * Returns:
382  *      0       success
383  *      errno   error encountered
384  *
385  */
386 int
387 scsp_hello_act_05(dcsp, msg)
388         Scsp_dcs        *dcsp;
389         Scsp_msg        *msg;
390 {
391         int     rc;
392         Scsp_id *ridp;
393
394         /*
395          * Null message pointer means message decode failed, so
396          * message must have been invalid.  Go to Waiting state.
397          */
398         if (msg == (Scsp_msg *)0) {
399                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
400                 HARP_CANCEL(&dcsp->sd_hello_rcv_t);
401                 return(0);
402         }
403
404         /*
405          * Ignore the message if it isn't a Hello
406          */
407         if (msg->sc_msg_type != SCSP_HELLO_MSG) {
408                 return(0);
409         }
410
411         /*
412          * Save relevant information about DCS, but don't let him give
413          * us zero for timeout values
414          */
415         if (msg->sc_hello->hello_int) {
416                 dcsp->sd_hello_int = msg->sc_hello->hello_int;
417         } else {
418                 dcsp->sd_hello_int = 1;
419         }
420         if (msg->sc_hello->dead_factor) {
421                 dcsp->sd_hello_df = msg->sc_hello->dead_factor;
422         } else {
423                 dcsp->sd_hello_df = 1;
424         }
425         dcsp->sd_dcsid = msg->sc_hello->hello_mcp.sid;
426
427         /*
428          * Check the message for the local server's ID
429          */
430         for (ridp = &msg->sc_hello->hello_mcp.rid;
431                         ridp;
432                         ridp = ridp->next) {
433                 if (scsp_cmp_id(&dcsp->sd_server->ss_lsid, ridp) == 0) {
434                         /*
435                          * Cancel and restart the receive timer
436                          */
437                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
438                         HARP_TIMER(&dcsp->sd_hello_rcv_t,
439                                         dcsp->sd_hello_int * dcsp->sd_hello_df,
440                                         scsp_hello_rcv_timeout);
441
442                         /*
443                          * Go to Bidirectional state and notify the
444                          * CA FSM that the connection is up
445                          */
446                         dcsp->sd_hello_state = SCSP_HFSM_BI_DIR;
447                         rc = scsp_cafsm(dcsp,
448                                         SCSP_CAFSM_HELLO_UP,
449                                         (void *)0);
450                         return(rc);
451                 }
452         }
453
454         /*
455          * We weren't in the receiver ID list, so go to
456          * Unidirectional state
457          */
458         dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
459
460         return(0);
461 }
462
463
464 /*
465  * HELLO finite state machine action 6
466  * Message received -- if message is not a HELLO, pass it to the CA
467  * FSM;  otherwise, if local server is not in receiver list, notify
468  * CA FSM and go to Unidirectional state
469  *
470  * Arguments:
471  *      dcsp    pointer to DCS control block
472  *      msg     pointer to received message
473  *
474  * Returns:
475  *      0       success
476  *      errno   error encountered
477  *
478  */
479 int
480 scsp_hello_act_06(dcsp, msg)
481         Scsp_dcs        *dcsp;
482         Scsp_msg        *msg;
483 {
484         int     rc = 0, rcv_found;
485         Scsp_id *ridp;
486
487         /*
488          * Null message pointer means message decode failed, so
489          * message must have been invalid.  Go to Waiting state.
490          */
491         if (msg == (Scsp_msg *)0) {
492                 HARP_CANCEL(&dcsp->sd_hello_rcv_t);
493                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
494                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
495                 return(rc);
496         }
497
498         /*
499          * Process the message depending on its type
500          */
501         switch(msg->sc_msg_type) {
502         case SCSP_CA_MSG:
503                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CA_MSG, (void *)msg);
504                 break;
505         case SCSP_CSU_REQ_MSG:
506                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REQ, (void *)msg);
507                 break;
508         case SCSP_CSU_REPLY_MSG:
509                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REPLY,
510                                 (void *)msg);
511                 break;
512         case SCSP_CSUS_MSG:
513                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_MSG, (void *)msg);
514                 break;
515         case SCSP_HELLO_MSG:
516                 /*
517                  * Make sure DCS info is consistent.  The sender ID,
518                  * family ID, protocol ID, and server group ID are
519                  * checked.
520                  */
521                 if (scsp_cmp_id(&msg->sc_hello->hello_mcp.sid,
522                                         &dcsp->sd_dcsid) ||
523                                 (msg->sc_hello->family_id !=
524                                         dcsp->sd_server->ss_fid) ||
525                                 (msg->sc_hello->hello_mcp.pid !=
526                                         dcsp->sd_server->ss_pid) ||
527                                 (msg->sc_hello->hello_mcp.sgid !=
528                                         dcsp->sd_server->ss_sgid)) {
529                         /*
530                          * Bad info--revert to waiting state
531                          */
532                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
533                         dcsp->sd_hello_state = SCSP_HFSM_WAITING;
534                         rc = scsp_cafsm(dcsp,
535                                         SCSP_CAFSM_HELLO_DOWN,
536                                         (void *)0);
537                         return(rc);
538                 }
539
540                 /*
541                  * Mark the arrival of the Hello message
542                  */
543                 dcsp->sd_hello_rcvd = 1;
544
545                 /*
546                  * Check the message for the local server's ID
547                  */
548                 for (ridp = &msg->sc_hello->hello_mcp.rid,
549                                         rcv_found = 0;
550                                 ridp;
551                                 ridp = ridp->next) {
552                         rcv_found = (scsp_cmp_id(ridp,
553                                 &dcsp->sd_server->ss_lsid) == 0);
554                 }
555
556                 if (rcv_found) {
557                         /*
558                          * The LS ID was in the list of receiver IDs--
559                          * Reset the Hello receive timer
560                          */
561                         dcsp->sd_hello_rcvd = 0;
562                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
563                         HARP_TIMER(&dcsp->sd_hello_rcv_t,
564                                         dcsp->sd_hello_int *
565                                                 dcsp->sd_hello_df,
566                                         scsp_hello_rcv_timeout);
567                 }
568                 break;
569         }
570
571         return(rc);
572 }