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.
49 * Randomize fsm id on link/init.
50 * Deal with variable outgoing MTU.
55 #include <sys/types.h>
61 static void fsm_timeout (void *);
62 static void fsm_rconfreq (fsm *, int, u_char *, int);
63 static void fsm_rconfack (fsm *, int, u_char *, int);
64 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
65 static void fsm_rtermreq (fsm *, int, u_char *, int);
66 static void fsm_rtermack (fsm *);
67 static void fsm_rcoderej (fsm *, u_char *, int);
68 static void fsm_sconfreq (fsm *, int);
70 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
72 int peer_mru[NUM_PPP];
76 * fsm_init - Initialize fsm.
78 * Initialize fsm state.
85 f->id = 0; /* XXX Start with random id? */
86 f->timeouttime = DEFTIMEOUT;
87 f->maxconfreqtransmits = DEFMAXCONFREQS;
88 f->maxtermtransmits = DEFMAXTERMREQS;
89 f->maxnakloops = DEFMAXNAKLOOPS;
90 f->term_reason_len = 0;
95 * fsm_lowerup - The lower layer is up.
106 if( f->flags & OPT_SILENT )
109 /* Send an initial configure-request */
116 FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
122 * fsm_lowerdown - The lower layer is down.
124 * Cancel all timeouts and inform upper layers.
127 fsm_lowerdown(fsm *f)
136 if( f->callbacks->starting )
137 (*f->callbacks->starting)(f);
142 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
150 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
154 if( f->callbacks->down )
155 (*f->callbacks->down)(f);
160 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
166 * fsm_open - Link is allowed to come up.
174 if( f->callbacks->starting )
175 (*f->callbacks->starting)(f);
179 if( f->flags & OPT_SILENT )
182 /* Send an initial configure-request */
193 if( f->flags & OPT_RESTART ){
202 * terminate_layer - Start process of shutting down the FSM
204 * Cancel any timeout running, notify upper layers we're done, and
205 * send a terminate-request message as configured.
208 terminate_layer(fsm *f, int nextstate)
210 if( f->state != OPENED )
211 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
212 else if( f->callbacks->down )
213 (*f->callbacks->down)(f); /* Inform upper layers we're down */
215 /* Init restart counter and send Terminate-Request */
216 f->retransmits = f->maxtermtransmits;
217 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
218 (u_char *) f->term_reason, f->term_reason_len);
220 if (f->retransmits == 0) {
222 * User asked for no terminate requests at all; just close it.
223 * We've already fired off one Terminate-Request just to be nice
224 * to the peer, but we're not going to wait for a reply.
226 f->state = nextstate == CLOSING ? CLOSED : STOPPED;
227 if( f->callbacks->finished )
228 (*f->callbacks->finished)(f);
232 TIMEOUT(fsm_timeout, f, f->timeouttime);
235 f->state = nextstate;
239 * fsm_close - Start closing connection.
241 * Cancel timeouts and either initiate close or possibly go directly to
245 fsm_close(fsm *f, char *reason)
247 f->term_reason = reason;
248 f->term_reason_len = (reason == NULL? 0: strlen(reason));
264 terminate_layer(f, CLOSING);
271 * fsm_timeout - Timeout expired.
274 fsm_timeout(void *arg)
276 fsm *f = (fsm *) arg;
281 if( f->retransmits <= 0 ){
283 * We've waited for an ack long enough. Peer probably heard us.
285 f->state = (f->state == CLOSING)? CLOSED: STOPPED;
286 if( f->callbacks->finished )
287 (*f->callbacks->finished)(f);
289 /* Send Terminate-Request */
290 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
291 (u_char *) f->term_reason, f->term_reason_len);
292 TIMEOUT(fsm_timeout, f, f->timeouttime);
300 if (f->retransmits <= 0) {
301 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
303 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
304 (*f->callbacks->finished)(f);
307 /* Retransmit the configure-request */
308 if (f->callbacks->retransmit)
309 (*f->callbacks->retransmit)(f);
310 fsm_sconfreq(f, 1); /* Re-send Configure-Request */
311 if( f->state == ACKRCVD )
317 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
323 * fsm_input - Input packet.
326 fsm_input(fsm *f, u_char *inpacket, int l)
333 * Parse header (code, id and length).
334 * If packet too short, drop it.
338 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
344 if (len < HEADERLEN) {
345 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
349 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
352 len -= HEADERLEN; /* subtract header length */
354 if( f->state == INITIAL || f->state == STARTING ){
355 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
356 f->protocol, f->state));
361 * Action depends on code.
365 fsm_rconfreq(f, id, inp, len);
369 fsm_rconfack(f, id, inp, len);
374 fsm_rconfnakrej(f, code, id, inp, len);
378 fsm_rtermreq(f, id, inp, len);
386 fsm_rcoderej(f, inp, len);
390 if( !f->callbacks->extcode
391 || !(*f->callbacks->extcode)(f, code, id, inp, len) )
392 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
399 * fsm_rconfreq - Receive Configure-Request.
402 fsm_rconfreq(fsm *f, int id, u_char *inp, int len)
404 int code, reject_if_disagree;
408 /* Go away, we're closed */
409 fsm_sdata(f, TERMACK, id, NULL, 0);
416 /* Go down and restart negotiation */
417 if( f->callbacks->down )
418 (*f->callbacks->down)(f); /* Inform upper layers */
419 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
424 /* Negotiation started by our peer */
425 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
431 * Pass the requested configuration options
432 * to protocol-specific code for checking.
434 if (f->callbacks->reqci){ /* Check CI */
435 reject_if_disagree = (f->nakloops >= f->maxnakloops);
436 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
438 code = CONFREJ; /* Reject all CI */
442 /* send the Ack, Nak or Rej to the peer */
443 fsm_sdata(f, code, id, inp, len);
445 if (code == CONFACK) {
446 if (f->state == ACKRCVD) {
447 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
449 if (f->callbacks->up)
450 (*f->callbacks->up)(f); /* Inform upper layers */
456 /* we sent CONFNAK or CONFREJ */
457 if (f->state != ACKRCVD)
459 if( code == CONFNAK )
466 * fsm_rconfack - Receive Configure-Ack.
469 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
471 if (id != f->reqid || f->seen_ack) /* Expected id? */
472 return; /* Nope, toss... */
473 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
475 /* Ack is bad - ignore it */
476 error("Received bad configure-ack: %P", inp, len);
485 fsm_sdata(f, TERMACK, id, NULL, 0);
490 f->retransmits = f->maxconfreqtransmits;
494 /* Huh? an extra valid Ack? oh well... */
495 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
501 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
503 f->retransmits = f->maxconfreqtransmits;
504 if (f->callbacks->up)
505 (*f->callbacks->up)(f); /* Inform upper layers */
509 /* Go down and restart negotiation */
510 if (f->callbacks->down)
511 (*f->callbacks->down)(f); /* Inform upper layers */
512 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
520 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
523 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
528 if (id != f->reqid || f->seen_ack) /* Expected id? */
529 return; /* Nope, toss... */
531 if (code == CONFNAK) {
533 treat_as_reject = (f->rnakloops >= f->maxnakloops);
534 if (f->callbacks->nakci == NULL
535 || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
536 error("Received bad configure-nak: %P", inp, len);
541 if (f->callbacks->rejci == NULL
542 || !(ret = f->callbacks->rejci(f, inp, len))) {
543 error("Received bad configure-rej: %P", inp, len);
553 fsm_sdata(f, TERMACK, id, NULL, 0);
558 /* They didn't agree to what we wanted - try another request */
559 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
561 f->state = STOPPED; /* kludge for stopping CCP */
563 fsm_sconfreq(f, 0); /* Send Configure-Request */
567 /* Got a Nak/reject when we had already had an Ack?? oh well... */
568 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
574 /* Go down and restart negotiation */
575 if (f->callbacks->down)
576 (*f->callbacks->down)(f); /* Inform upper layers */
577 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
585 * fsm_rtermreq - Receive Terminate-Req.
588 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
593 f->state = REQSENT; /* Start over but keep trying */
598 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
600 info("%s terminated by peer", PROTO_NAME(f));
603 if (f->callbacks->down)
604 (*f->callbacks->down)(f); /* Inform upper layers */
605 TIMEOUT(fsm_timeout, f, f->timeouttime);
609 fsm_sdata(f, TERMACK, id, NULL, 0);
614 * fsm_rtermack - Receive Terminate-Ack.
621 UNTIMEOUT(fsm_timeout, f);
623 if( f->callbacks->finished )
624 (*f->callbacks->finished)(f);
627 UNTIMEOUT(fsm_timeout, f);
629 if( f->callbacks->finished )
630 (*f->callbacks->finished)(f);
638 if (f->callbacks->down)
639 (*f->callbacks->down)(f); /* Inform upper layers */
648 * fsm_rcoderej - Receive an Code-Reject.
651 fsm_rcoderej(fsm *f, u_char *inp, int len)
655 if (len < HEADERLEN) {
656 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
661 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
663 if( f->state == ACKRCVD )
669 * fsm_protreject - Peer doesn't speak this protocol.
671 * Treat this as a catastrophic error (RXJ-).
674 fsm_protreject(fsm *f)
678 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
682 if( f->callbacks->finished )
683 (*f->callbacks->finished)(f);
690 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
694 if( f->callbacks->finished )
695 (*f->callbacks->finished)(f);
699 terminate_layer(f, STOPPING);
703 FSMDEBUG(("%s: Protocol-reject event in state %d!",
704 PROTO_NAME(f), f->state));
710 * fsm_sconfreq - Send a Configure-Request.
713 fsm_sconfreq(fsm *f, int retransmit)
718 if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
719 /* Not currently negotiating - reset options */
720 if( f->callbacks->resetci )
721 (*f->callbacks->resetci)(f);
727 /* New request - reset retransmission counter, use new ID */
728 f->retransmits = f->maxconfreqtransmits;
735 * Make up the request packet
737 outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
738 if( f->callbacks->cilen && f->callbacks->addci ){
739 cilen = (*f->callbacks->cilen)(f);
740 if( cilen > peer_mru[f->unit] - HEADERLEN )
741 cilen = peer_mru[f->unit] - HEADERLEN;
742 if (f->callbacks->addci)
743 (*f->callbacks->addci)(f, outp, &cilen);
747 /* send the request to our peer */
748 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
750 /* start the retransmit timer */
752 TIMEOUT(fsm_timeout, f, f->timeouttime);
757 * fsm_sdata - Send some data.
759 * Used for all packets sent to our peer by this module.
762 fsm_sdata(fsm *f, int code, int id, u_char *data, int datalen)
767 /* Adjust length to be smaller than MTU */
768 outp = outpacket_buf;
769 if (datalen > peer_mru[f->unit] - HEADERLEN)
770 datalen = peer_mru[f->unit] - HEADERLEN;
771 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
772 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
773 outlen = datalen + HEADERLEN;
774 MAKEHEADER(outp, f->protocol);
777 PUTSHORT(outlen, outp);
778 output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);