2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright (c) 1989 Carnegie Mellon University.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by Carnegie Mellon University. The name of the
13 * University may not be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 static char rcsid
[] = "$FreeBSD$";
26 * Randomize fsm id on link/init.
27 * Deal with variable outgoing MTU.
32 #include <sys/types.h>
38 static void fsm_timeout(void *);
39 static void fsm_rconfreq(fsm
*, int, u_char
*, int);
40 static void fsm_rconfack(fsm
*, int, u_char
*, int);
41 static void fsm_rconfnakrej(fsm
*, int, int, u_char
*, int);
42 static void fsm_rtermreq(fsm
*, int, u_char
*, int);
43 static void fsm_rtermack(fsm
*);
44 static void fsm_rcoderej(fsm
*, u_char
*, int);
45 static void fsm_sconfreq(fsm
*, int);
47 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
49 int peer_mru
[NUM_PPP
];
53 * fsm_init - Initialize fsm.
55 * Initialize fsm state.
63 f
->id
= 0; /* XXX Start with random id? */
64 f
->timeouttime
= DEFTIMEOUT
;
65 f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
66 f
->maxtermtransmits
= DEFMAXTERMREQS
;
67 f
->maxnakloops
= DEFMAXNAKLOOPS
;
68 f
->term_reason_len
= 0;
73 * fsm_lowerup - The lower layer is up.
85 if( f
->flags
& OPT_SILENT
)
88 /* Send an initial configure-request */
95 FSMDEBUG((LOG_INFO
, "%s: Up event in state %d!",
96 PROTO_NAME(f
), f
->state
));
102 * fsm_lowerdown - The lower layer is down.
104 * Cancel all timeouts and inform upper layers.
117 if( f
->callbacks
->starting
)
118 (*f
->callbacks
->starting
)(f
);
123 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
131 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
135 if( f
->callbacks
->down
)
136 (*f
->callbacks
->down
)(f
);
141 FSMDEBUG((LOG_INFO
, "%s: Down event in state %d!",
142 PROTO_NAME(f
), f
->state
));
148 * fsm_open - Link is allowed to come up.
157 if( f
->callbacks
->starting
)
158 (*f
->callbacks
->starting
)(f
);
162 if( f
->flags
& OPT_SILENT
)
165 /* Send an initial configure-request */
176 if( f
->flags
& OPT_RESTART
){
186 * fsm_close - Start closing connection.
188 * Cancel timeouts and either initiate close or possibly go directly to
196 f
->term_reason
= reason
;
197 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
213 if( f
->state
!= OPENED
)
214 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
215 else if( f
->callbacks
->down
)
216 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
218 /* Init restart counter, send Terminate-Request */
219 f
->retransmits
= f
->maxtermtransmits
;
220 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
221 (u_char
*) f
->term_reason
, f
->term_reason_len
);
222 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
232 * fsm_timeout - Timeout expired.
238 fsm
*f
= (fsm
*) arg
;
243 if( f
->retransmits
<= 0 ){
245 * We've waited for an ack long enough. Peer probably heard us.
247 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
248 if( f
->callbacks
->finished
)
249 (*f
->callbacks
->finished
)(f
);
251 /* Send Terminate-Request */
252 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
253 (u_char
*) f
->term_reason
, f
->term_reason_len
);
254 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
262 if (f
->retransmits
<= 0) {
263 syslog(LOG_WARNING
, "%s: timeout sending Config-Requests",
266 if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
267 (*f
->callbacks
->finished
)(f
);
270 /* Retransmit the configure-request */
271 if (f
->callbacks
->retransmit
)
272 (*f
->callbacks
->retransmit
)(f
);
273 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
274 if( f
->state
== ACKRCVD
)
280 FSMDEBUG((LOG_INFO
, "%s: Timeout event in state %d!",
281 PROTO_NAME(f
), f
->state
));
287 * fsm_input - Input packet.
290 fsm_input(f
, inpacket
, l
)
300 * Parse header (code, id and length).
301 * If packet too short, drop it.
305 FSMDEBUG((LOG_WARNING
, "fsm_input(%x): Rcvd short header.",
312 if (len
< HEADERLEN
) {
313 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd illegal length.",
318 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd short packet.",
322 len
-= HEADERLEN
; /* subtract header length */
324 if( f
->state
== INITIAL
|| f
->state
== STARTING
){
325 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd packet in state %d.",
326 f
->protocol
, f
->state
));
331 * Action depends on code.
335 fsm_rconfreq(f
, id
, inp
, len
);
339 fsm_rconfack(f
, id
, inp
, len
);
344 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
348 fsm_rtermreq(f
, id
, inp
, len
);
356 fsm_rcoderej(f
, inp
, len
);
360 if( !f
->callbacks
->extcode
361 || !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
362 fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
369 * fsm_rconfreq - Receive Configure-Request.
372 fsm_rconfreq(f
, id
, inp
, len
)
378 int code
, reject_if_disagree
;
380 FSMDEBUG((LOG_INFO
, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f
), id
));
383 /* Go away, we're closed */
384 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
391 /* Go down and restart negotiation */
392 if( f
->callbacks
->down
)
393 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
394 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
398 /* Negotiation started by our peer */
399 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
405 * Pass the requested configuration options
406 * to protocol-specific code for checking.
408 if (f
->callbacks
->reqci
){ /* Check CI */
409 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
410 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
412 code
= CONFREJ
; /* Reject all CI */
416 /* send the Ack, Nak or Rej to the peer */
417 fsm_sdata(f
, code
, id
, inp
, len
);
419 if (code
== CONFACK
) {
420 if (f
->state
== ACKRCVD
) {
421 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
423 if (f
->callbacks
->up
)
424 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
430 /* we sent CONFACK or CONFREJ */
431 if (f
->state
!= ACKRCVD
)
433 if( code
== CONFNAK
)
440 * fsm_rconfack - Receive Configure-Ack.
443 fsm_rconfack(f
, id
, inp
, len
)
449 FSMDEBUG((LOG_INFO
, "fsm_rconfack(%s): Rcvd id %d.",
452 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
453 return; /* Nope, toss... */
454 if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
456 /* Ack is bad - ignore it */
457 log_packet(inp
, len
, "Received bad configure-ack: ", LOG_ERR
);
458 FSMDEBUG((LOG_INFO
, "%s: received bad Ack (length %d)",
459 PROTO_NAME(f
), len
));
467 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
472 f
->retransmits
= f
->maxconfreqtransmits
;
476 /* Huh? an extra valid Ack? oh well... */
477 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
483 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
485 f
->retransmits
= f
->maxconfreqtransmits
;
486 if (f
->callbacks
->up
)
487 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
491 /* Go down and restart negotiation */
492 if (f
->callbacks
->down
)
493 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
494 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
502 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
505 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
511 int (*proc
)(fsm
*, u_char
*, int);
514 FSMDEBUG((LOG_INFO
, "fsm_rconfnakrej(%s): Rcvd id %d.",
517 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
518 return; /* Nope, toss... */
519 proc
= (code
== CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
520 if (!proc
|| !(ret
= proc(f
, inp
, len
))) {
521 /* Nak/reject is bad - ignore it */
522 log_packet(inp
, len
, "Received bad configure-nak/rej: ", LOG_ERR
);
523 FSMDEBUG((LOG_INFO
, "%s: received bad %s (length %d)",
524 PROTO_NAME(f
), (code
==CONFNAK
? "Nak": "reject"), len
));
532 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
537 /* They didn't agree to what we wanted - try another request */
538 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
540 f
->state
= STOPPED
; /* kludge for stopping CCP */
542 fsm_sconfreq(f
, 0); /* Send Configure-Request */
546 /* Got a Nak/reject when we had already had an Ack?? oh well... */
547 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
553 /* Go down and restart negotiation */
554 if (f
->callbacks
->down
)
555 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
556 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
564 * fsm_rtermreq - Receive Terminate-Req.
567 fsm_rtermreq(f
, id
, p
, len
)
575 FSMDEBUG((LOG_INFO
, "fsm_rtermreq(%s): Rcvd id %d.",
581 f
->state
= REQSENT
; /* Start over but keep trying */
586 fmtmsg(str
, sizeof(str
), "%0.*v", len
, p
);
587 syslog(LOG_INFO
, "%s terminated by peer (%s)", PROTO_NAME(f
), str
);
589 syslog(LOG_INFO
, "%s terminated by peer", PROTO_NAME(f
));
590 if (f
->callbacks
->down
)
591 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
594 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
598 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
603 * fsm_rtermack - Receive Terminate-Ack.
609 FSMDEBUG((LOG_INFO
, "fsm_rtermack(%s).", PROTO_NAME(f
)));
613 UNTIMEOUT(fsm_timeout
, f
);
615 if( f
->callbacks
->finished
)
616 (*f
->callbacks
->finished
)(f
);
619 UNTIMEOUT(fsm_timeout
, f
);
621 if( f
->callbacks
->finished
)
622 (*f
->callbacks
->finished
)(f
);
630 if (f
->callbacks
->down
)
631 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
639 * fsm_rcoderej - Receive a Code-Reject.
642 fsm_rcoderej(f
, inp
, len
)
649 FSMDEBUG((LOG_INFO
, "fsm_rcoderej(%s).", PROTO_NAME(f
)));
651 if (len
< HEADERLEN
) {
652 FSMDEBUG((LOG_INFO
, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
657 syslog(LOG_WARNING
, "%s: Rcvd Code-Reject for code %d, id %d",
658 PROTO_NAME(f
), code
, id
);
660 if( f
->state
== ACKRCVD
)
666 * fsm_protreject - Peer doesn't speak this protocol.
668 * Treat this as a catastrophic error (RXJ-).
676 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
680 if( f
->callbacks
->finished
)
681 (*f
->callbacks
->finished
)(f
);
688 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
692 if( f
->callbacks
->finished
)
693 (*f
->callbacks
->finished
)(f
);
697 if( f
->callbacks
->down
)
698 (*f
->callbacks
->down
)(f
);
700 /* Init restart counter, send Terminate-Request */
701 f
->retransmits
= f
->maxtermtransmits
;
702 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
703 (u_char
*) f
->term_reason
, f
->term_reason_len
);
704 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
711 FSMDEBUG((LOG_INFO
, "%s: Protocol-reject event in state %d!",
712 PROTO_NAME(f
), f
->state
));
718 * fsm_sconfreq - Send a Configure-Request.
721 fsm_sconfreq(f
, retransmit
)
728 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
729 /* Not currently negotiating - reset options */
730 if( f
->callbacks
->resetci
)
731 (*f
->callbacks
->resetci
)(f
);
736 /* New request - reset retransmission counter, use new ID */
737 f
->retransmits
= f
->maxconfreqtransmits
;
744 * Make up the request packet
746 outp
= outpacket_buf
+ PPP_HDRLEN
+ HEADERLEN
;
747 if( f
->callbacks
->cilen
&& f
->callbacks
->addci
){
748 cilen
= (*f
->callbacks
->cilen
)(f
);
749 if( cilen
> peer_mru
[f
->unit
] - HEADERLEN
)
750 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
751 if (f
->callbacks
->addci
)
752 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
756 /* send the request to our peer */
757 fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
759 /* start the retransmit timer */
761 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
763 FSMDEBUG((LOG_INFO
, "%s: sending Configure-Request, id %d",
764 PROTO_NAME(f
), f
->reqid
));
769 * fsm_sdata - Send some data.
771 * Used for all packets sent to our peer by this module.
774 fsm_sdata(f
, code
, id
, data
, datalen
)
783 /* Adjust length to be smaller than MTU */
784 outp
= outpacket_buf
;
785 if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
786 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
787 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
788 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
789 outlen
= datalen
+ HEADERLEN
;
790 MAKEHEADER(outp
, f
->protocol
);
793 PUTSHORT(outlen
, outp
);
794 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
796 FSMDEBUG((LOG_INFO
, "fsm_sdata(%s): Sent code %d, id %d.",
797 PROTO_NAME(f
), code
, id
));