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.
19 * $FreeBSD: src/usr.sbin/pppd/fsm.c,v 1.8 1999/08/28 01:19:02 peter Exp $
20 * $DragonFly: src/usr.sbin/pppd/fsm.c,v 1.4 2005/11/24 23:42:54 swildner Exp $
25 * Randomize fsm id on link/init.
26 * Deal with variable outgoing MTU.
31 #include <sys/types.h>
37 static void fsm_timeout(void *);
38 static void fsm_rconfreq(fsm
*, int, u_char
*, int);
39 static void fsm_rconfack(fsm
*, int, u_char
*, int);
40 static void fsm_rconfnakrej(fsm
*, int, int, u_char
*, int);
41 static void fsm_rtermreq(fsm
*, int, u_char
*, int);
42 static void fsm_rtermack(fsm
*);
43 static void fsm_rcoderej(fsm
*, u_char
*, int);
44 static void fsm_sconfreq(fsm
*, int);
46 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
48 int peer_mru
[NUM_PPP
];
52 * fsm_init - Initialize fsm.
54 * Initialize fsm state.
61 f
->id
= 0; /* XXX Start with random id? */
62 f
->timeouttime
= DEFTIMEOUT
;
63 f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
64 f
->maxtermtransmits
= DEFMAXTERMREQS
;
65 f
->maxnakloops
= DEFMAXNAKLOOPS
;
66 f
->term_reason_len
= 0;
71 * fsm_lowerup - The lower layer is up.
82 if( f
->flags
& OPT_SILENT
)
85 /* Send an initial configure-request */
92 FSMDEBUG((LOG_INFO
, "%s: Up event in state %d!",
93 PROTO_NAME(f
), f
->state
));
99 * fsm_lowerdown - The lower layer is down.
101 * Cancel all timeouts and inform upper layers.
104 fsm_lowerdown(fsm
*f
)
113 if( f
->callbacks
->starting
)
114 (*f
->callbacks
->starting
)(f
);
119 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
127 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
131 if( f
->callbacks
->down
)
132 (*f
->callbacks
->down
)(f
);
137 FSMDEBUG((LOG_INFO
, "%s: Down event in state %d!",
138 PROTO_NAME(f
), f
->state
));
144 * fsm_open - Link is allowed to come up.
152 if( f
->callbacks
->starting
)
153 (*f
->callbacks
->starting
)(f
);
157 if( f
->flags
& OPT_SILENT
)
160 /* Send an initial configure-request */
171 if( f
->flags
& OPT_RESTART
){
181 * fsm_close - Start closing connection.
183 * Cancel timeouts and either initiate close or possibly go directly to
187 fsm_close(fsm
*f
, char *reason
)
189 f
->term_reason
= reason
;
190 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
206 if( f
->state
!= OPENED
)
207 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
208 else if( f
->callbacks
->down
)
209 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
211 /* Init restart counter, send Terminate-Request */
212 f
->retransmits
= f
->maxtermtransmits
;
213 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
214 (u_char
*) f
->term_reason
, f
->term_reason_len
);
215 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
225 * fsm_timeout - Timeout expired.
228 fsm_timeout(void *arg
)
230 fsm
*f
= (fsm
*) arg
;
235 if( f
->retransmits
<= 0 ){
237 * We've waited for an ack long enough. Peer probably heard us.
239 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
240 if( f
->callbacks
->finished
)
241 (*f
->callbacks
->finished
)(f
);
243 /* Send Terminate-Request */
244 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
245 (u_char
*) f
->term_reason
, f
->term_reason_len
);
246 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
254 if (f
->retransmits
<= 0) {
255 syslog(LOG_WARNING
, "%s: timeout sending Config-Requests",
258 if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
259 (*f
->callbacks
->finished
)(f
);
262 /* Retransmit the configure-request */
263 if (f
->callbacks
->retransmit
)
264 (*f
->callbacks
->retransmit
)(f
);
265 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
266 if( f
->state
== ACKRCVD
)
272 FSMDEBUG((LOG_INFO
, "%s: Timeout event in state %d!",
273 PROTO_NAME(f
), f
->state
));
279 * fsm_input - Input packet.
282 fsm_input(fsm
*f
, u_char
*inpacket
, int l
)
289 * Parse header (code, id and length).
290 * If packet too short, drop it.
294 FSMDEBUG((LOG_WARNING
, "fsm_input(%x): Rcvd short header.",
301 if (len
< HEADERLEN
) {
302 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd illegal length.",
307 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd short packet.",
311 len
-= HEADERLEN
; /* subtract header length */
313 if( f
->state
== INITIAL
|| f
->state
== STARTING
){
314 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd packet in state %d.",
315 f
->protocol
, f
->state
));
320 * Action depends on code.
324 fsm_rconfreq(f
, id
, inp
, len
);
328 fsm_rconfack(f
, id
, inp
, len
);
333 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
337 fsm_rtermreq(f
, id
, inp
, len
);
345 fsm_rcoderej(f
, inp
, len
);
349 if( !f
->callbacks
->extcode
350 || !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
351 fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
358 * fsm_rconfreq - Receive Configure-Request.
361 fsm_rconfreq(fsm
*f
, int id
, u_char
*inp
, int len
)
363 int code
, reject_if_disagree
;
365 FSMDEBUG((LOG_INFO
, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f
), id
));
368 /* Go away, we're closed */
369 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
376 /* Go down and restart negotiation */
377 if( f
->callbacks
->down
)
378 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
379 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
383 /* Negotiation started by our peer */
384 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
390 * Pass the requested configuration options
391 * to protocol-specific code for checking.
393 if (f
->callbacks
->reqci
){ /* Check CI */
394 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
395 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
397 code
= CONFREJ
; /* Reject all CI */
401 /* send the Ack, Nak or Rej to the peer */
402 fsm_sdata(f
, code
, id
, inp
, len
);
404 if (code
== CONFACK
) {
405 if (f
->state
== ACKRCVD
) {
406 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
408 if (f
->callbacks
->up
)
409 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
415 /* we sent CONFACK or CONFREJ */
416 if (f
->state
!= ACKRCVD
)
418 if( code
== CONFNAK
)
425 * fsm_rconfack - Receive Configure-Ack.
428 fsm_rconfack(fsm
*f
, int id
, u_char
*inp
, int len
)
430 FSMDEBUG((LOG_INFO
, "fsm_rconfack(%s): Rcvd id %d.",
433 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
434 return; /* Nope, toss... */
435 if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
437 /* Ack is bad - ignore it */
438 log_packet(inp
, len
, "Received bad configure-ack: ", LOG_ERR
);
439 FSMDEBUG((LOG_INFO
, "%s: received bad Ack (length %d)",
440 PROTO_NAME(f
), len
));
448 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
453 f
->retransmits
= f
->maxconfreqtransmits
;
457 /* Huh? an extra valid Ack? oh well... */
458 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
464 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
466 f
->retransmits
= f
->maxconfreqtransmits
;
467 if (f
->callbacks
->up
)
468 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
472 /* Go down and restart negotiation */
473 if (f
->callbacks
->down
)
474 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
475 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
483 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
486 fsm_rconfnakrej(fsm
*f
, int code
, int id
, u_char
*inp
, int len
)
488 int (*proc
)(fsm
*, u_char
*, int);
491 FSMDEBUG((LOG_INFO
, "fsm_rconfnakrej(%s): Rcvd id %d.",
494 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
495 return; /* Nope, toss... */
496 proc
= (code
== CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
497 if (!proc
|| !(ret
= proc(f
, inp
, len
))) {
498 /* Nak/reject is bad - ignore it */
499 log_packet(inp
, len
, "Received bad configure-nak/rej: ", LOG_ERR
);
500 FSMDEBUG((LOG_INFO
, "%s: received bad %s (length %d)",
501 PROTO_NAME(f
), (code
==CONFNAK
? "Nak": "reject"), len
));
509 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
514 /* They didn't agree to what we wanted - try another request */
515 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
517 f
->state
= STOPPED
; /* kludge for stopping CCP */
519 fsm_sconfreq(f
, 0); /* Send Configure-Request */
523 /* Got a Nak/reject when we had already had an Ack?? oh well... */
524 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
530 /* Go down and restart negotiation */
531 if (f
->callbacks
->down
)
532 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
533 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
541 * fsm_rtermreq - Receive Terminate-Req.
544 fsm_rtermreq(fsm
*f
, int id
, u_char
*p
, int len
)
548 FSMDEBUG((LOG_INFO
, "fsm_rtermreq(%s): Rcvd id %d.",
554 f
->state
= REQSENT
; /* Start over but keep trying */
559 fmtmsg(str
, sizeof(str
), "%0.*v", len
, p
);
560 syslog(LOG_INFO
, "%s terminated by peer (%s)", PROTO_NAME(f
), str
);
562 syslog(LOG_INFO
, "%s terminated by peer", PROTO_NAME(f
));
563 if (f
->callbacks
->down
)
564 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
567 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
571 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
576 * fsm_rtermack - Receive Terminate-Ack.
581 FSMDEBUG((LOG_INFO
, "fsm_rtermack(%s).", PROTO_NAME(f
)));
585 UNTIMEOUT(fsm_timeout
, f
);
587 if( f
->callbacks
->finished
)
588 (*f
->callbacks
->finished
)(f
);
591 UNTIMEOUT(fsm_timeout
, f
);
593 if( f
->callbacks
->finished
)
594 (*f
->callbacks
->finished
)(f
);
602 if (f
->callbacks
->down
)
603 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
611 * fsm_rcoderej - Receive an Code-Reject.
614 fsm_rcoderej(fsm
*f
, u_char
*inp
, int len
)
618 FSMDEBUG((LOG_INFO
, "fsm_rcoderej(%s).", PROTO_NAME(f
)));
620 if (len
< HEADERLEN
) {
621 FSMDEBUG((LOG_INFO
, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
626 syslog(LOG_WARNING
, "%s: Rcvd Code-Reject for code %d, id %d",
627 PROTO_NAME(f
), code
, id
);
629 if( f
->state
== ACKRCVD
)
635 * fsm_protreject - Peer doesn't speak this protocol.
637 * Treat this as a catastrophic error (RXJ-).
640 fsm_protreject(fsm
*f
)
644 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
648 if( f
->callbacks
->finished
)
649 (*f
->callbacks
->finished
)(f
);
656 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
660 if( f
->callbacks
->finished
)
661 (*f
->callbacks
->finished
)(f
);
665 if( f
->callbacks
->down
)
666 (*f
->callbacks
->down
)(f
);
668 /* Init restart counter, send Terminate-Request */
669 f
->retransmits
= f
->maxtermtransmits
;
670 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
671 (u_char
*) f
->term_reason
, f
->term_reason_len
);
672 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
679 FSMDEBUG((LOG_INFO
, "%s: Protocol-reject event in state %d!",
680 PROTO_NAME(f
), f
->state
));
686 * fsm_sconfreq - Send a Configure-Request.
689 fsm_sconfreq(fsm
*f
, int retransmit
)
694 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
695 /* Not currently negotiating - reset options */
696 if( f
->callbacks
->resetci
)
697 (*f
->callbacks
->resetci
)(f
);
702 /* New request - reset retransmission counter, use new ID */
703 f
->retransmits
= f
->maxconfreqtransmits
;
710 * Make up the request packet
712 outp
= outpacket_buf
+ PPP_HDRLEN
+ HEADERLEN
;
713 if( f
->callbacks
->cilen
&& f
->callbacks
->addci
){
714 cilen
= (*f
->callbacks
->cilen
)(f
);
715 if( cilen
> peer_mru
[f
->unit
] - HEADERLEN
)
716 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
717 if (f
->callbacks
->addci
)
718 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
722 /* send the request to our peer */
723 fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
725 /* start the retransmit timer */
727 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
729 FSMDEBUG((LOG_INFO
, "%s: sending Configure-Request, id %d",
730 PROTO_NAME(f
), f
->reqid
));
735 * fsm_sdata - Send some data.
737 * Used for all packets sent to our peer by this module.
740 fsm_sdata(fsm
*f
, int code
, int id
, u_char
*data
, int datalen
)
745 /* Adjust length to be smaller than MTU */
746 outp
= outpacket_buf
;
747 if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
748 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
749 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
750 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
751 outlen
= datalen
+ HEADERLEN
;
752 MAKEHEADER(outp
, f
->protocol
);
755 PUTSHORT(outlen
, outp
);
756 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
758 FSMDEBUG((LOG_INFO
, "fsm_sdata(%s): Sent code %d, id %d.",
759 PROTO_NAME(f
), code
, id
));