Merge from vendor branch LIBARCHIVE:
[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.4 2003/11/15 20:33:43 eirikn 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(Scsp_dcs *, Scsp_msg *);
65 int     scsp_hello_act_01(Scsp_dcs *, Scsp_msg *);
66 int     scsp_hello_act_02(Scsp_dcs *, Scsp_msg *);
67 int     scsp_hello_act_03(Scsp_dcs *, Scsp_msg *);
68 int     scsp_hello_act_04(Scsp_dcs *, Scsp_msg *);
69 int     scsp_hello_act_05(Scsp_dcs *, Scsp_msg *);
70 int     scsp_hello_act_06(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(Scsp_dcs *dcsp, int event, Scsp_msg *msg)
109 {
110         int     action, rc, state;
111
112         /*
113          * Select an action from the state table
114          */
115         state = dcsp->sd_hello_state;
116         action = hello_state_table[event][state];
117         if (scsp_trace_mode & SCSP_TRACE_HFSM) {
118                 scsp_trace("HFSM: state=%d, event=%d, action=%d\n",
119                                 state, event, action);
120         }
121         if (action >= HELLO_ACTION_CNT || action <= 0) {
122                 scsp_log(LOG_ERR, "Hello FSM--invalid action %d; state=%d, event=%d",
123                                 action, dcsp->sd_hello_state, event);
124                 abort();
125         }
126
127         /*
128          * Perform the selected action
129          */
130         rc = scsp_action_vector[action](dcsp, msg);
131
132         return(rc);
133 }
134
135
136 /*
137  * HELLO finite state machine action 0
138  * Unexpected action -- log an error message
139  *
140  * Arguments:
141  *      dcsp    pointer to DCS control block
142  *      msg     pointer to received message (ignored)
143  *
144  * Returns:
145  *      EOPNOTSUPP      always returns EOPNOTSUPP
146  *
147  */
148 int
149 scsp_hello_act_00(Scsp_dcs *dcsp, Scsp_msg *msg)
150 {
151         scsp_log(LOG_ERR, "Hello FSM error--unexpected action, state=%d",
152                         dcsp->sd_hello_state);
153         return(EOPNOTSUPP);
154 }
155
156
157 /*
158  * HELLO finite state machine action 1
159  * VCC open -- send HELLO message, start hello timer, go to Waiting
160  * state
161  *
162  * Arguments:
163  *      dcsp    pointer to DCS control block
164  *      msg     pointer to received message (ignored)
165  *
166  * Returns:
167  *      0       success
168  *      errno   error encountered
169  *
170  */
171 int
172 scsp_hello_act_01(Scsp_dcs *dcsp, Scsp_msg *msg)
173 {
174         int             rc;
175
176         /*
177          * Cancel the VCC open timer if it's running
178          */
179         HARP_CANCEL(&dcsp->sd_open_t);
180
181         /*
182          * Go to Waiting state
183          */
184         dcsp->sd_hello_state = SCSP_HFSM_WAITING;
185
186         /*
187          * Send a Hello message
188          */
189         rc = scsp_send_hello(dcsp);
190         if (rc == 0) {
191                 /*
192                  * Success--start the Hello timer
193                  */
194                 HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
195                                 scsp_hello_timeout);
196         }
197
198         return(rc);
199 }
200
201
202 /*
203  * HELLO finite state machine action 2
204  * VCC closed -- notify CA FSM, go to Down state, try to re-open VCC
205  *
206  * Arguments:
207  *      dcsp    pointer to DCS control block
208  *      msg     pointer to received message (ignored)
209  *
210  * Returns:
211  *      0       success
212  *      errno   error encountered
213  *
214  */
215 int
216 scsp_hello_act_02(Scsp_dcs *dcsp, Scsp_msg *msg)
217 {
218         int             rc;
219
220         /*
221          * Cancel any current timers
222          */
223         HARP_CANCEL(&dcsp->sd_hello_h_t);
224         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
225
226         /*
227          * Log the loss of the VCC
228          */
229         if (dcsp->sd_hello_state > SCSP_HFSM_WAITING) {
230                 scsp_log(LOG_ERR, "VC to %s closed",
231                                 format_atm_addr(&dcsp->sd_addr));
232         }
233
234         /*
235          * Tell the CA FSM that the conection to the DCS is lost
236          */
237         rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
238
239         /*
240          * Go to Down state
241          */
242         dcsp->sd_hello_state = SCSP_HFSM_DOWN;
243
244         /*
245          * If our ID is lower than the DCS's, wait a second before
246          * trying to connect.  This should keep both of us from
247          * trying to connect at the same time, resulting in two
248          * VCCs being open.
249          */
250         if (scsp_cmp_id(&dcsp->sd_server->ss_lsid,
251                         &dcsp->sd_dcsid) < 0) {
252                 /*
253                  * Our ID is lower--start the VCC open timer for one
254                  * second so we'll try to open the VCC if the DCS
255                  * doesn't do it by then
256                  */
257                 HARP_TIMER(&dcsp->sd_open_t, 1, scsp_open_timeout);
258         } else {
259                 /*
260                  * Our ID is higher--try to reopen the VCC immediately
261                  */
262                 if (scsp_dcs_connect(dcsp)) {
263                         /*
264                          * Conncect failed -- set a timer and try
265                          * again later
266                          */
267                         HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
268                                         scsp_open_timeout);
269                 }
270         }
271
272         return(0);
273 }
274
275
276 /*
277  * HELLO finite state machine action 3
278  * Hello timer expired -- send HELLO message, restart hello timer
279  *
280  * Arguments:
281  *      dcsp    pointer to DCS control block
282  *      msg     pointer to received message (ignored)
283  *
284  * Returns:
285  *      0       success
286  *      errno   error encountered
287  *
288  */
289 int
290 scsp_hello_act_03(Scsp_dcs *dcsp, Scsp_msg *msg)
291 {
292         int             rc;
293
294         /*
295          * Send a Hello message
296          */
297         rc = scsp_send_hello(dcsp);
298         if (rc == 0) {
299                 /*
300                  * Success--restart the Hello timer
301                  */
302                 HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
303                                 scsp_hello_timeout);
304         }
305
306         return(rc);
307 }
308
309
310 /*
311  * HELLO finite state machine action 4
312  * Receive timer expired -- if we haven't received any Hellos, notify
313  * CA FSM and go to Waiting state;  if we've received Hellos, but we
314  * weren't in the receiver ID list, go to Unidirectional state
315  *
316  * Arguments:
317  *      dcsp    pointer to DCS control block
318  *      msg     pointer to received message (ignored)
319  *
320  * Returns:
321  *      0       success
322  *      errno   error encountered
323  *
324  */
325 int
326 scsp_hello_act_04(Scsp_dcs *dcsp, Scsp_msg *msg)
327 {
328         int     rc = 0;
329
330         /*
331          * Check whether we'ver received any Hellos lately
332          */
333         if (dcsp->sd_hello_rcvd) {
334                 /*
335                  * We've had Hellos since the receive timer was
336                  * started--go to Unidirectional state
337                  */
338                 dcsp->sd_hello_rcvd = 0;
339                 dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
340         } else {
341                 /*
342                  * We haven't seen any Hellos at all from the DCS in
343                  * hello_interval * dead_factor seconds--go to Waiting
344                  * state
345                  */
346                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
347         }
348
349         /*
350          * Notify the CA FSM
351          */
352         rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
353
354         return(rc);
355 }
356
357
358 /*
359  * HELLO finite state machine action 5
360  * Message received -- Ignore all but HELLO messages;  if local server
361  * is in receiver list, notify CA FSM and go to Bidirectional state;
362  * otherwise, go to Unidirectional state
363  *
364  * Arguments:
365  *      dcsp    pointer to DCS control block
366  *      msg     pointer to received message
367  *
368  * Returns:
369  *      0       success
370  *      errno   error encountered
371  *
372  */
373 int
374 scsp_hello_act_05(Scsp_dcs *dcsp, Scsp_msg *msg)
375 {
376         int     rc;
377         Scsp_id *ridp;
378
379         /*
380          * Null message pointer means message decode failed, so
381          * message must have been invalid.  Go to Waiting state.
382          */
383         if (msg == (Scsp_msg *)0) {
384                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
385                 HARP_CANCEL(&dcsp->sd_hello_rcv_t);
386                 return(0);
387         }
388
389         /*
390          * Ignore the message if it isn't a Hello
391          */
392         if (msg->sc_msg_type != SCSP_HELLO_MSG) {
393                 return(0);
394         }
395
396         /*
397          * Save relevant information about DCS, but don't let him give
398          * us zero for timeout values
399          */
400         if (msg->sc_hello->hello_int) {
401                 dcsp->sd_hello_int = msg->sc_hello->hello_int;
402         } else {
403                 dcsp->sd_hello_int = 1;
404         }
405         if (msg->sc_hello->dead_factor) {
406                 dcsp->sd_hello_df = msg->sc_hello->dead_factor;
407         } else {
408                 dcsp->sd_hello_df = 1;
409         }
410         dcsp->sd_dcsid = msg->sc_hello->hello_mcp.sid;
411
412         /*
413          * Check the message for the local server's ID
414          */
415         for (ridp = &msg->sc_hello->hello_mcp.rid;
416                         ridp;
417                         ridp = ridp->next) {
418                 if (scsp_cmp_id(&dcsp->sd_server->ss_lsid, ridp) == 0) {
419                         /*
420                          * Cancel and restart the receive timer
421                          */
422                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
423                         HARP_TIMER(&dcsp->sd_hello_rcv_t,
424                                         dcsp->sd_hello_int * dcsp->sd_hello_df,
425                                         scsp_hello_rcv_timeout);
426
427                         /*
428                          * Go to Bidirectional state and notify the
429                          * CA FSM that the connection is up
430                          */
431                         dcsp->sd_hello_state = SCSP_HFSM_BI_DIR;
432                         rc = scsp_cafsm(dcsp,
433                                         SCSP_CAFSM_HELLO_UP,
434                                         (void *)0);
435                         return(rc);
436                 }
437         }
438
439         /*
440          * We weren't in the receiver ID list, so go to
441          * Unidirectional state
442          */
443         dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
444
445         return(0);
446 }
447
448
449 /*
450  * HELLO finite state machine action 6
451  * Message received -- if message is not a HELLO, pass it to the CA
452  * FSM;  otherwise, if local server is not in receiver list, notify
453  * CA FSM and go to Unidirectional state
454  *
455  * Arguments:
456  *      dcsp    pointer to DCS control block
457  *      msg     pointer to received message
458  *
459  * Returns:
460  *      0       success
461  *      errno   error encountered
462  *
463  */
464 int
465 scsp_hello_act_06(Scsp_dcs *dcsp, Scsp_msg *msg)
466 {
467         int     rc = 0, rcv_found;
468         Scsp_id *ridp;
469
470         /*
471          * Null message pointer means message decode failed, so
472          * message must have been invalid.  Go to Waiting state.
473          */
474         if (msg == (Scsp_msg *)0) {
475                 HARP_CANCEL(&dcsp->sd_hello_rcv_t);
476                 dcsp->sd_hello_state = SCSP_HFSM_WAITING;
477                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
478                 return(rc);
479         }
480
481         /*
482          * Process the message depending on its type
483          */
484         switch(msg->sc_msg_type) {
485         case SCSP_CA_MSG:
486                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CA_MSG, (void *)msg);
487                 break;
488         case SCSP_CSU_REQ_MSG:
489                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REQ, (void *)msg);
490                 break;
491         case SCSP_CSU_REPLY_MSG:
492                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REPLY,
493                                 (void *)msg);
494                 break;
495         case SCSP_CSUS_MSG:
496                 rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_MSG, (void *)msg);
497                 break;
498         case SCSP_HELLO_MSG:
499                 /*
500                  * Make sure DCS info is consistent.  The sender ID,
501                  * family ID, protocol ID, and server group ID are
502                  * checked.
503                  */
504                 if (scsp_cmp_id(&msg->sc_hello->hello_mcp.sid,
505                                         &dcsp->sd_dcsid) ||
506                                 (msg->sc_hello->family_id !=
507                                         dcsp->sd_server->ss_fid) ||
508                                 (msg->sc_hello->hello_mcp.pid !=
509                                         dcsp->sd_server->ss_pid) ||
510                                 (msg->sc_hello->hello_mcp.sgid !=
511                                         dcsp->sd_server->ss_sgid)) {
512                         /*
513                          * Bad info--revert to waiting state
514                          */
515                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
516                         dcsp->sd_hello_state = SCSP_HFSM_WAITING;
517                         rc = scsp_cafsm(dcsp,
518                                         SCSP_CAFSM_HELLO_DOWN,
519                                         (void *)0);
520                         return(rc);
521                 }
522
523                 /*
524                  * Mark the arrival of the Hello message
525                  */
526                 dcsp->sd_hello_rcvd = 1;
527
528                 /*
529                  * Check the message for the local server's ID
530                  */
531                 for (ridp = &msg->sc_hello->hello_mcp.rid,
532                                         rcv_found = 0;
533                                 ridp;
534                                 ridp = ridp->next) {
535                         rcv_found = (scsp_cmp_id(ridp,
536                                 &dcsp->sd_server->ss_lsid) == 0);
537                 }
538
539                 if (rcv_found) {
540                         /*
541                          * The LS ID was in the list of receiver IDs--
542                          * Reset the Hello receive timer
543                          */
544                         dcsp->sd_hello_rcvd = 0;
545                         HARP_CANCEL(&dcsp->sd_hello_rcv_t);
546                         HARP_TIMER(&dcsp->sd_hello_rcv_t,
547                                         dcsp->sd_hello_int *
548                                                 dcsp->sd_hello_df,
549                                         scsp_hello_rcv_timeout);
550                 }
551                 break;
552         }
553
554         return(rc);
555 }