2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * 3. The name "Carnegie Mellon University" must not be used to
19 * endorse or promote products derived from this software without
20 * prior written permission. For permission or any legal
21 * details, please contact
22 * Office of Technology Transfer
23 * Carnegie Mellon University
25 * Pittsburgh, PA 15213-3890
26 * (412) 268-4387, fax: (412) 268-7395
27 * tech-transfer@andrew.cmu.edu
29 * 4. Redistributions of any form whatsoever must retain the following
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45 * Randomize fsm id on link/init.
46 * Deal with variable outgoing MTU.
51 #include <sys/types.h>
57 static void fsm_timeout (void *);
58 static void fsm_rconfreq (fsm *, int, u_char *, int);
59 static void fsm_rconfack (fsm *, int, u_char *, int);
60 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
61 static void fsm_rtermreq (fsm *, int, u_char *, int);
62 static void fsm_rtermack (fsm *);
63 static void fsm_rcoderej (fsm *, u_char *, int);
64 static void fsm_sconfreq (fsm *, int);
66 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
68 int peer_mru[NUM_PPP];
72 * fsm_init - Initialize fsm.
74 * Initialize fsm state.
81 f->id = 0; /* XXX Start with random id? */
82 f->timeouttime = DEFTIMEOUT;
83 f->maxconfreqtransmits = DEFMAXCONFREQS;
84 f->maxtermtransmits = DEFMAXTERMREQS;
85 f->maxnakloops = DEFMAXNAKLOOPS;
86 f->term_reason_len = 0;
91 * fsm_lowerup - The lower layer is up.
102 if( f->flags & OPT_SILENT )
105 /* Send an initial configure-request */
112 FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
118 * fsm_lowerdown - The lower layer is down.
120 * Cancel all timeouts and inform upper layers.
123 fsm_lowerdown(fsm *f)
132 if( f->callbacks->starting )
133 (*f->callbacks->starting)(f);
138 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
146 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
150 if( f->callbacks->down )
151 (*f->callbacks->down)(f);
156 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
162 * fsm_open - Link is allowed to come up.
170 if( f->callbacks->starting )
171 (*f->callbacks->starting)(f);
175 if( f->flags & OPT_SILENT )
178 /* Send an initial configure-request */
189 if( f->flags & OPT_RESTART ){
198 * terminate_layer - Start process of shutting down the FSM
200 * Cancel any timeout running, notify upper layers we're done, and
201 * send a terminate-request message as configured.
204 terminate_layer(fsm *f, int nextstate)
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 and 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);
216 if (f->retransmits == 0) {
218 * User asked for no terminate requests at all; just close it.
219 * We've already fired off one Terminate-Request just to be nice
220 * to the peer, but we're not going to wait for a reply.
222 f->state = nextstate == CLOSING ? CLOSED : STOPPED;
223 if( f->callbacks->finished )
224 (*f->callbacks->finished)(f);
228 TIMEOUT(fsm_timeout, f, f->timeouttime);
231 f->state = nextstate;
235 * fsm_close - Start closing connection.
237 * Cancel timeouts and either initiate close or possibly go directly to
241 fsm_close(fsm *f, char *reason)
243 f->term_reason = reason;
244 f->term_reason_len = (reason == NULL? 0: strlen(reason));
260 terminate_layer(f, CLOSING);
267 * fsm_timeout - Timeout expired.
270 fsm_timeout(void *arg)
272 fsm *f = (fsm *) arg;
277 if( f->retransmits <= 0 ){
279 * We've waited for an ack long enough. Peer probably heard us.
281 f->state = (f->state == CLOSING)? CLOSED: STOPPED;
282 if( f->callbacks->finished )
283 (*f->callbacks->finished)(f);
285 /* Send Terminate-Request */
286 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
287 (u_char *) f->term_reason, f->term_reason_len);
288 TIMEOUT(fsm_timeout, f, f->timeouttime);
296 if (f->retransmits <= 0) {
297 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
299 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
300 (*f->callbacks->finished)(f);
303 /* Retransmit the configure-request */
304 if (f->callbacks->retransmit)
305 (*f->callbacks->retransmit)(f);
306 fsm_sconfreq(f, 1); /* Re-send Configure-Request */
307 if( f->state == ACKRCVD )
313 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
319 * fsm_input - Input packet.
322 fsm_input(fsm *f, u_char *inpacket, int l)
329 * Parse header (code, id and length).
330 * If packet too short, drop it.
334 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
340 if (len < HEADERLEN) {
341 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
345 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
348 len -= HEADERLEN; /* subtract header length */
350 if( f->state == INITIAL || f->state == STARTING ){
351 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
352 f->protocol, f->state));
357 * Action depends on code.
361 fsm_rconfreq(f, id, inp, len);
365 fsm_rconfack(f, id, inp, len);
370 fsm_rconfnakrej(f, code, id, inp, len);
374 fsm_rtermreq(f, id, inp, len);
382 fsm_rcoderej(f, inp, len);
386 if( !f->callbacks->extcode
387 || !(*f->callbacks->extcode)(f, code, id, inp, len) )
388 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
395 * fsm_rconfreq - Receive Configure-Request.
398 fsm_rconfreq(fsm *f, int id, u_char *inp, int len)
400 int code, reject_if_disagree;
404 /* Go away, we're closed */
405 fsm_sdata(f, TERMACK, id, NULL, 0);
412 /* Go down and restart negotiation */
413 if( f->callbacks->down )
414 (*f->callbacks->down)(f); /* Inform upper layers */
415 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
420 /* Negotiation started by our peer */
421 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
427 * Pass the requested configuration options
428 * to protocol-specific code for checking.
430 if (f->callbacks->reqci){ /* Check CI */
431 reject_if_disagree = (f->nakloops >= f->maxnakloops);
432 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
434 code = CONFREJ; /* Reject all CI */
438 /* send the Ack, Nak or Rej to the peer */
439 fsm_sdata(f, code, id, inp, len);
441 if (code == CONFACK) {
442 if (f->state == ACKRCVD) {
443 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
445 if (f->callbacks->up)
446 (*f->callbacks->up)(f); /* Inform upper layers */
452 /* we sent CONFNAK or CONFREJ */
453 if (f->state != ACKRCVD)
455 if( code == CONFNAK )
462 * fsm_rconfack - Receive Configure-Ack.
465 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
467 if (id != f->reqid || f->seen_ack) /* Expected id? */
468 return; /* Nope, toss... */
469 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
471 /* Ack is bad - ignore it */
472 error("Received bad configure-ack: %P", inp, len);
481 fsm_sdata(f, TERMACK, id, NULL, 0);
486 f->retransmits = f->maxconfreqtransmits;
490 /* Huh? an extra valid Ack? oh well... */
491 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
497 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
499 f->retransmits = f->maxconfreqtransmits;
500 if (f->callbacks->up)
501 (*f->callbacks->up)(f); /* Inform upper layers */
505 /* Go down and restart negotiation */
506 if (f->callbacks->down)
507 (*f->callbacks->down)(f); /* Inform upper layers */
508 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
516 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
519 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
524 if (id != f->reqid || f->seen_ack) /* Expected id? */
525 return; /* Nope, toss... */
527 if (code == CONFNAK) {
529 treat_as_reject = (f->rnakloops >= f->maxnakloops);
530 if (f->callbacks->nakci == NULL
531 || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
532 error("Received bad configure-nak: %P", inp, len);
537 if (f->callbacks->rejci == NULL
538 || !(ret = f->callbacks->rejci(f, inp, len))) {
539 error("Received bad configure-rej: %P", inp, len);
549 fsm_sdata(f, TERMACK, id, NULL, 0);
554 /* They didn't agree to what we wanted - try another request */
555 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
557 f->state = STOPPED; /* kludge for stopping CCP */
559 fsm_sconfreq(f, 0); /* Send Configure-Request */
563 /* Got a Nak/reject when we had already had an Ack?? oh well... */
564 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
570 /* Go down and restart negotiation */
571 if (f->callbacks->down)
572 (*f->callbacks->down)(f); /* Inform upper layers */
573 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
581 * fsm_rtermreq - Receive Terminate-Req.
584 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
589 f->state = REQSENT; /* Start over but keep trying */
594 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
596 info("%s terminated by peer", PROTO_NAME(f));
599 if (f->callbacks->down)
600 (*f->callbacks->down)(f); /* Inform upper layers */
601 TIMEOUT(fsm_timeout, f, f->timeouttime);
605 fsm_sdata(f, TERMACK, id, NULL, 0);
610 * fsm_rtermack - Receive Terminate-Ack.
617 UNTIMEOUT(fsm_timeout, f);
619 if( f->callbacks->finished )
620 (*f->callbacks->finished)(f);
623 UNTIMEOUT(fsm_timeout, f);
625 if( f->callbacks->finished )
626 (*f->callbacks->finished)(f);
634 if (f->callbacks->down)
635 (*f->callbacks->down)(f); /* Inform upper layers */
644 * fsm_rcoderej - Receive an Code-Reject.
647 fsm_rcoderej(fsm *f, u_char *inp, int len)
651 if (len < HEADERLEN) {
652 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
657 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
659 if( f->state == ACKRCVD )
665 * fsm_protreject - Peer doesn't speak this protocol.
667 * Treat this as a catastrophic error (RXJ-).
670 fsm_protreject(fsm *f)
674 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
678 if( f->callbacks->finished )
679 (*f->callbacks->finished)(f);
686 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
690 if( f->callbacks->finished )
691 (*f->callbacks->finished)(f);
695 terminate_layer(f, STOPPING);
699 FSMDEBUG(("%s: Protocol-reject event in state %d!",
700 PROTO_NAME(f), f->state));
706 * fsm_sconfreq - Send a Configure-Request.
709 fsm_sconfreq(fsm *f, int retransmit)
714 if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
715 /* Not currently negotiating - reset options */
716 if( f->callbacks->resetci )
717 (*f->callbacks->resetci)(f);
723 /* New request - reset retransmission counter, use new ID */
724 f->retransmits = f->maxconfreqtransmits;
731 * Make up the request packet
733 outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
734 if( f->callbacks->cilen && f->callbacks->addci ){
735 cilen = (*f->callbacks->cilen)(f);
736 if( cilen > peer_mru[f->unit] - HEADERLEN )
737 cilen = peer_mru[f->unit] - HEADERLEN;
738 if (f->callbacks->addci)
739 (*f->callbacks->addci)(f, outp, &cilen);
743 /* send the request to our peer */
744 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
746 /* start the retransmit timer */
748 TIMEOUT(fsm_timeout, f, f->timeouttime);
753 * fsm_sdata - Send some data.
755 * Used for all packets sent to our peer by this module.
758 fsm_sdata(fsm *f, int code, int id, u_char *data, int datalen)
763 /* Adjust length to be smaller than MTU */
764 outp = outpacket_buf;
765 if (datalen > peer_mru[f->unit] - HEADERLEN)
766 datalen = peer_mru[f->unit] - HEADERLEN;
767 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
768 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
769 outlen = datalen + HEADERLEN;
770 MAKEHEADER(outp, f->protocol);
773 PUTSHORT(outlen, outp);
774 output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);