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