1 /*****************************************************************************
2 * fsm.c - Network Control Protocol Finite State Machine program file.
3 *
4 * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
5 * portions Copyright (c) 1997 by Global Election Systems Inc.
6 *
7 * The authors hereby grant permission to use, copy, modify, distribute,
8 * and license this software and its documentation for any purpose, provided
9 * that existing copyright notices are retained in all copies and that this
10 * notice and the following disclaimer are included verbatim in any
11 * distributions. No written agreement, license, or royalty fee is required
12 * for any of the authorized uses.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 ******************************************************************************
26 * REVISION HISTORY
27 *
28 * 03-01-01 Marc Boucher <marc@mbsi.ca>
29 *   Ported to lwIP.
30 * 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
31 *   Original based on BSD fsm.c.
32 *****************************************************************************/
33 /*
34  * fsm.c - {Link, IP} Control Protocol Finite State Machine.
35  *
36  * Copyright (c) 1989 Carnegie Mellon University.
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms are permitted
40  * provided that the above copyright notice and this paragraph are
41  * duplicated in all such forms and that any documentation,
42  * advertising materials, and other materials related to such
43  * distribution and use acknowledge that the software was developed
44  * by Carnegie Mellon University.  The name of the
45  * University may not be used to endorse or promote products derived
46  * from this software without specific prior written permission.
47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50  */
51 
52 /*
53  * TODO:
54  * Randomize fsm id on link/init.
55  * Deal with variable outgoing MTU.
56  */
57 
58 #include "lwip/opt.h"
59 
60 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
61 
62 #include "ppp_impl.h"
63 #include "pppdebug.h"
64 
65 #include "fsm.h"
66 
67 #include <string.h>
68 
69 #if PPP_DEBUG
70 static const char *ppperr_strerr[] = {
71            "LS_INITIAL",  /* LS_INITIAL  0 */
72            "LS_STARTING", /* LS_STARTING 1 */
73            "LS_CLOSED",   /* LS_CLOSED   2 */
74            "LS_STOPPED",  /* LS_STOPPED  3 */
75            "LS_CLOSING",  /* LS_CLOSING  4 */
76            "LS_STOPPING", /* LS_STOPPING 5 */
77            "LS_REQSENT",  /* LS_REQSENT  6 */
78            "LS_ACKRCVD",  /* LS_ACKRCVD  7 */
79            "LS_ACKSENT",  /* LS_ACKSENT  8 */
80            "LS_OPENED"    /* LS_OPENED   9 */
81 };
82 #endif /* PPP_DEBUG */
83 
84 static void fsm_timeout (void *);
85 static void fsm_rconfreq (fsm *, u_char, u_char *, int);
86 static void fsm_rconfack (fsm *, int, u_char *, int);
87 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
88 static void fsm_rtermreq (fsm *, int, u_char *, int);
89 static void fsm_rtermack (fsm *);
90 static void fsm_rcoderej (fsm *, u_char *, int);
91 static void fsm_sconfreq (fsm *, int);
92 
93 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
94 
95 int peer_mru[NUM_PPP];
96 
97 
98 /*
99  * fsm_init - Initialize fsm.
100  *
101  * Initialize fsm state.
102  */
103 void
104 fsm_init(fsm *f)
105 {
106   f->state = LS_INITIAL;
107   f->flags = 0;
108   f->id = 0;        /* XXX Start with random id? */
109   f->timeouttime = FSM_DEFTIMEOUT;
110   f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
111   f->maxtermtransmits = FSM_DEFMAXTERMREQS;
112   f->maxnakloops = FSM_DEFMAXNAKLOOPS;
113   f->term_reason_len = 0;
114 }
115 
116 
117 /*
118  * fsm_lowerup - The lower layer is up.
119  */
120 void
121 fsm_lowerup(fsm *f)
122 {
123   int oldState = f->state;
124 
125   LWIP_UNUSED_ARG(oldState);
126 
127   switch( f->state ) {
128     case LS_INITIAL:
129       f->state = LS_CLOSED;
130       break;
131 
132     case LS_STARTING:
133       if( f->flags & OPT_SILENT ) {
134         f->state = LS_STOPPED;
135       } else {
136         /* Send an initial configure-request */
137         fsm_sconfreq(f, 0);
138         f->state = LS_REQSENT;
139       }
140     break;
141 
142     default:
143       FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
144           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
145   }
146 
147   FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
148       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
149 }
150 
151 
152 /*
153  * fsm_lowerdown - The lower layer is down.
154  *
155  * Cancel all timeouts and inform upper layers.
156  */
157 void
158 fsm_lowerdown(fsm *f)
159 {
160   int oldState = f->state;
161 
162   LWIP_UNUSED_ARG(oldState);
163 
164   switch( f->state ) {
165     case LS_CLOSED:
166       f->state = LS_INITIAL;
167       break;
168 
169     case LS_STOPPED:
170       f->state = LS_STARTING;
171       if( f->callbacks->starting ) {
172         (*f->callbacks->starting)(f);
173       }
174       break;
175 
176     case LS_CLOSING:
177       f->state = LS_INITIAL;
178       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
179       break;
180 
181     case LS_STOPPING:
182     case LS_REQSENT:
183     case LS_ACKRCVD:
184     case LS_ACKSENT:
185       f->state = LS_STARTING;
186       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
187       break;
188 
189     case LS_OPENED:
190       if( f->callbacks->down ) {
191         (*f->callbacks->down)(f);
192       }
193       f->state = LS_STARTING;
194       break;
195 
196     default:
197       FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
198           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
199   }
200 
201   FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
202       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
203 }
204 
205 
206 /*
207  * fsm_open - Link is allowed to come up.
208  */
209 void
210 fsm_open(fsm *f)
211 {
212   int oldState = f->state;
213 
214   LWIP_UNUSED_ARG(oldState);
215 
216   switch( f->state ) {
217     case LS_INITIAL:
218       f->state = LS_STARTING;
219       if( f->callbacks->starting ) {
220         (*f->callbacks->starting)(f);
221       }
222       break;
223 
224     case LS_CLOSED:
225       if( f->flags & OPT_SILENT ) {
226         f->state = LS_STOPPED;
227       } else {
228         /* Send an initial configure-request */
229         fsm_sconfreq(f, 0);
230         f->state = LS_REQSENT;
231       }
232       break;
233 
234     case LS_CLOSING:
235       f->state = LS_STOPPING;
236       /* fall through */
237     case LS_STOPPED:
238     case LS_OPENED:
239       if( f->flags & OPT_RESTART ) {
240         fsm_lowerdown(f);
241         fsm_lowerup(f);
242       }
243       break;
244   }
245 
246   FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
247       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
248 }
249 
250 #if 0 /* backport pppd 2.4.4b1; */
251 /*
252  * terminate_layer - Start process of shutting down the FSM
253  *
254  * Cancel any timeout running, notify upper layers we're done, and
255  * send a terminate-request message as configured.
256  */
257 static void
258 terminate_layer(fsm *f, int nextstate)
259 {
260   /* @todo */
261 }
262 #endif
263 
264 /*
265  * fsm_close - Start closing connection.
266  *
267  * Cancel timeouts and either initiate close or possibly go directly to
268  * the LS_CLOSED state.
269  */
270 void
271 fsm_close(fsm *f, char *reason)
272 {
273   int oldState = f->state;
274 
275   LWIP_UNUSED_ARG(oldState);
276 
277   f->term_reason = reason;
278   f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
279   switch( f->state ) {
280     case LS_STARTING:
281       f->state = LS_INITIAL;
282       break;
283     case LS_STOPPED:
284       f->state = LS_CLOSED;
285       break;
286     case LS_STOPPING:
287       f->state = LS_CLOSING;
288       break;
289 
290     case LS_REQSENT:
291     case LS_ACKRCVD:
292     case LS_ACKSENT:
293     case LS_OPENED:
294       if( f->state != LS_OPENED ) {
295         UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
296       } else if( f->callbacks->down ) {
297         (*f->callbacks->down)(f);  /* Inform upper layers we're down */
298       }
299       /* Init restart counter, send Terminate-Request */
300       f->retransmits = f->maxtermtransmits;
301       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
302             (u_char *) f->term_reason, f->term_reason_len);
303       TIMEOUT(fsm_timeout, f, f->timeouttime);
304       --f->retransmits;
305 
306       f->state = LS_CLOSING;
307       break;
308   }
309 
310   FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
311       PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
312 }
313 
314 
315 /*
316  * fsm_timeout - Timeout expired.
317  */
318 static void
319 fsm_timeout(void *arg)
320 {
321   fsm *f = (fsm *) arg;
322 
323   switch (f->state) {
324     case LS_CLOSING:
325     case LS_STOPPING:
326       if( f->retransmits <= 0 ) {
327         FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
328              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
329         /*
330          * We've waited for an ack long enough.  Peer probably heard us.
331          */
332         f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
333         if( f->callbacks->finished ) {
334           (*f->callbacks->finished)(f);
335         }
336       } else {
337         FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
338              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
339         /* Send Terminate-Request */
340         fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
341             (u_char *) f->term_reason, f->term_reason_len);
342         TIMEOUT(fsm_timeout, f, f->timeouttime);
343         --f->retransmits;
344       }
345       break;
346 
347     case LS_REQSENT:
348     case LS_ACKRCVD:
349     case LS_ACKSENT:
350       if (f->retransmits <= 0) {
351         FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
352          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
353         f->state = LS_STOPPED;
354         if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
355           (*f->callbacks->finished)(f);
356         }
357       } else {
358         FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
359          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
360         /* Retransmit the configure-request */
361         if (f->callbacks->retransmit) {
362           (*f->callbacks->retransmit)(f);
363         }
364         fsm_sconfreq(f, 1);    /* Re-send Configure-Request */
365         if( f->state == LS_ACKRCVD ) {
366           f->state = LS_REQSENT;
367         }
368       }
369       break;
370 
371     default:
372       FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
373           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
374   }
375 }
376 
377 
378 /*
379  * fsm_input - Input packet.
380  */
381 void
382 fsm_input(fsm *f, u_char *inpacket, int l)
383 {
384   u_char *inp = inpacket;
385   u_char code, id;
386   int len;
387 
388   /*
389   * Parse header (code, id and length).
390   * If packet too short, drop it.
391   */
392   if (l < HEADERLEN) {
393     FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
394           f->protocol));
395     return;
396   }
397   GETCHAR(code, inp);
398   GETCHAR(id, inp);
399   GETSHORT(len, inp);
400   if (len < HEADERLEN) {
401     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
402         f->protocol));
403     return;
404   }
405   if (len > l) {
406     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
407         f->protocol));
408     return;
409   }
410   len -= HEADERLEN;    /* subtract header length */
411 
412   if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
413     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
414         f->protocol, f->state, ppperr_strerr[f->state]));
415     return;
416   }
417   FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
418   /*
419    * Action depends on code.
420    */
421   switch (code) {
422     case CONFREQ:
423       fsm_rconfreq(f, id, inp, len);
424       break;
425 
426     case CONFACK:
427       fsm_rconfack(f, id, inp, len);
428       break;
429 
430     case CONFNAK:
431     case CONFREJ:
432       fsm_rconfnakrej(f, code, id, inp, len);
433       break;
434 
435     case TERMREQ:
436       fsm_rtermreq(f, id, inp, len);
437       break;
438 
439     case TERMACK:
440       fsm_rtermack(f);
441       break;
442 
443     case CODEREJ:
444       fsm_rcoderej(f, inp, len);
445       break;
446 
447     default:
448       FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
449       if( !f->callbacks->extcode ||
450           !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
451         fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
452       }
453       break;
454   }
455 }
456 
457 
458 /*
459  * fsm_rconfreq - Receive Configure-Request.
460  */
461 static void
462 fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
463 {
464   int code, reject_if_disagree;
465 
466   FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
467         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
468   switch( f->state ) {
469     case LS_CLOSED:
470       /* Go away, we're closed */
471       fsm_sdata(f, TERMACK, id, NULL, 0);
472       return;
473     case LS_CLOSING:
474     case LS_STOPPING:
475       return;
476 
477     case LS_OPENED:
478       /* Go down and restart negotiation */
479       if( f->callbacks->down ) {
480         (*f->callbacks->down)(f);  /* Inform upper layers */
481       }
482       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
483       break;
484 
485     case LS_STOPPED:
486       /* Negotiation started by our peer */
487       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
488       f->state = LS_REQSENT;
489       break;
490   }
491 
492   /*
493   * Pass the requested configuration options
494   * to protocol-specific code for checking.
495   */
496   if (f->callbacks->reqci) {    /* Check CI */
497     reject_if_disagree = (f->nakloops >= f->maxnakloops);
498     code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
499   } else if (len) {
500     code = CONFREJ;      /* Reject all CI */
501   } else {
502     code = CONFACK;
503   }
504 
505   /* send the Ack, Nak or Rej to the peer */
506   fsm_sdata(f, (u_char)code, id, inp, len);
507 
508   if (code == CONFACK) {
509     if (f->state == LS_ACKRCVD) {
510       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
511       f->state = LS_OPENED;
512       if (f->callbacks->up) {
513         (*f->callbacks->up)(f);  /* Inform upper layers */
514       }
515     } else {
516       f->state = LS_ACKSENT;
517     }
518     f->nakloops = 0;
519   } else {
520     /* we sent CONFACK or CONFREJ */
521     if (f->state != LS_ACKRCVD) {
522       f->state = LS_REQSENT;
523     }
524     if( code == CONFNAK ) {
525       ++f->nakloops;
526     }
527   }
528 }
529 
530 
531 /*
532  * fsm_rconfack - Receive Configure-Ack.
533  */
534 static void
535 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
536 {
537   FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
538         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
539 
540   if (id != f->reqid || f->seen_ack) {   /* Expected id? */
541     return; /* Nope, toss... */
542   }
543   if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
544     /* Ack is bad - ignore it */
545     FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
546           PROTO_NAME(f), len));
547     return;
548   }
549   f->seen_ack = 1;
550 
551   switch (f->state) {
552     case LS_CLOSED:
553     case LS_STOPPED:
554       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
555       break;
556 
557     case LS_REQSENT:
558       f->state = LS_ACKRCVD;
559       f->retransmits = f->maxconfreqtransmits;
560       break;
561 
562     case LS_ACKRCVD:
563       /* Huh? an extra valid Ack? oh well... */
564       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
565       fsm_sconfreq(f, 0);
566       f->state = LS_REQSENT;
567       break;
568 
569     case LS_ACKSENT:
570       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
571       f->state = LS_OPENED;
572       f->retransmits = f->maxconfreqtransmits;
573       if (f->callbacks->up) {
574         (*f->callbacks->up)(f);  /* Inform upper layers */
575       }
576       break;
577 
578     case LS_OPENED:
579       /* Go down and restart negotiation */
580       if (f->callbacks->down) {
581         (*f->callbacks->down)(f);  /* Inform upper layers */
582       }
583       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
584       f->state = LS_REQSENT;
585       break;
586   }
587 }
588 
589 
590 /*
591  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
592  */
593 static void
594 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
595 {
596   int (*proc) (fsm *, u_char *, int);
597   int ret;
598 
599   FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
600         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
601 
602   if (id != f->reqid || f->seen_ack) { /* Expected id? */
603     return;        /* Nope, toss... */
604   }
605   proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
606   if (!proc || !((ret = proc(f, inp, len)))) {
607     /* Nak/reject is bad - ignore it */
608     FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
609           PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
610     return;
611   }
612   f->seen_ack = 1;
613 
614   switch (f->state) {
615     case LS_CLOSED:
616     case LS_STOPPED:
617       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
618       break;
619 
620     case LS_REQSENT:
621     case LS_ACKSENT:
622       /* They didn't agree to what we wanted - try another request */
623       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
624       if (ret < 0) {
625         f->state = LS_STOPPED;    /* kludge for stopping CCP */
626       } else {
627         fsm_sconfreq(f, 0);    /* Send Configure-Request */
628       }
629       break;
630 
631     case LS_ACKRCVD:
632       /* Got a Nak/reject when we had already had an Ack?? oh well... */
633       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
634       fsm_sconfreq(f, 0);
635       f->state = LS_REQSENT;
636       break;
637 
638     case LS_OPENED:
639       /* Go down and restart negotiation */
640       if (f->callbacks->down) {
641         (*f->callbacks->down)(f);  /* Inform upper layers */
642       }
643       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
644       f->state = LS_REQSENT;
645       break;
646   }
647 }
648 
649 
650 /*
651  * fsm_rtermreq - Receive Terminate-Req.
652  */
653 static void
654 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
655 {
656   LWIP_UNUSED_ARG(p);
657 
658   FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
659         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
660 
661   switch (f->state) {
662     case LS_ACKRCVD:
663     case LS_ACKSENT:
664       f->state = LS_REQSENT;    /* Start over but keep trying */
665       break;
666 
667     case LS_OPENED:
668       if (len > 0) {
669         FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
670       } else {
671         FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
672       }
673       if (f->callbacks->down) {
674         (*f->callbacks->down)(f);  /* Inform upper layers */
675       }
676       f->retransmits = 0;
677       f->state = LS_STOPPING;
678       TIMEOUT(fsm_timeout, f, f->timeouttime);
679       break;
680   }
681 
682   fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
683 }
684 
685 
686 /*
687  * fsm_rtermack - Receive Terminate-Ack.
688  */
689 static void
690 fsm_rtermack(fsm *f)
691 {
692   FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
693         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
694 
695   switch (f->state) {
696     case LS_CLOSING:
697       UNTIMEOUT(fsm_timeout, f);
698       f->state = LS_CLOSED;
699       if( f->callbacks->finished ) {
700         (*f->callbacks->finished)(f);
701       }
702       break;
703 
704     case LS_STOPPING:
705       UNTIMEOUT(fsm_timeout, f);
706       f->state = LS_STOPPED;
707       if( f->callbacks->finished ) {
708         (*f->callbacks->finished)(f);
709       }
710       break;
711 
712     case LS_ACKRCVD:
713       f->state = LS_REQSENT;
714       break;
715 
716     case LS_OPENED:
717       if (f->callbacks->down) {
718         (*f->callbacks->down)(f);  /* Inform upper layers */
719       }
720       fsm_sconfreq(f, 0);
721       break;
722     default:
723       FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
724                 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
725   }
726 }
727 
728 
729 /*
730  * fsm_rcoderej - Receive an Code-Reject.
731  */
732 static void
733 fsm_rcoderej(fsm *f, u_char *inp, int len)
734 {
735   u_char code, id;
736 
737   FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
738         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
739 
740   if (len < HEADERLEN) {
741     FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
742     return;
743   }
744   GETCHAR(code, inp);
745   GETCHAR(id, inp);
746   FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
747         PROTO_NAME(f), code, id));
748 
749   if( f->state == LS_ACKRCVD ) {
750     f->state = LS_REQSENT;
751   }
752 }
753 
754 
755 /*
756  * fsm_protreject - Peer doesn't speak this protocol.
757  *
758  * Treat this as a catastrophic error (RXJ-).
759  */
760 void
761 fsm_protreject(fsm *f)
762 {
763   switch( f->state ) {
764     case LS_CLOSING:
765       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
766       /* fall through */
767     case LS_CLOSED:
768       f->state = LS_CLOSED;
769       if( f->callbacks->finished ) {
770         (*f->callbacks->finished)(f);
771       }
772       break;
773 
774     case LS_STOPPING:
775     case LS_REQSENT:
776     case LS_ACKRCVD:
777     case LS_ACKSENT:
778       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
779       /* fall through */
780     case LS_STOPPED:
781       f->state = LS_STOPPED;
782       if( f->callbacks->finished ) {
783         (*f->callbacks->finished)(f);
784       }
785       break;
786 
787     case LS_OPENED:
788       if( f->callbacks->down ) {
789         (*f->callbacks->down)(f);
790       }
791       /* Init restart counter, send Terminate-Request */
792       f->retransmits = f->maxtermtransmits;
793       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
794             (u_char *) f->term_reason, f->term_reason_len);
795       TIMEOUT(fsm_timeout, f, f->timeouttime);
796       --f->retransmits;
797 
798       f->state = LS_STOPPING;
799       break;
800 
801     default:
802       FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
803             PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
804     }
805 }
806 
807 
808 /*
809  * fsm_sconfreq - Send a Configure-Request.
810  */
811 static void
812 fsm_sconfreq(fsm *f, int retransmit)
813 {
814   u_char *outp;
815   int cilen;
816 
817   if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
818     /* Not currently negotiating - reset options */
819     if( f->callbacks->resetci ) {
820       (*f->callbacks->resetci)(f);
821     }
822     f->nakloops = 0;
823   }
824 
825   if( !retransmit ) {
826     /* New request - reset retransmission counter, use new ID */
827     f->retransmits = f->maxconfreqtransmits;
828     f->reqid = ++f->id;
829   }
830 
831   f->seen_ack = 0;
832 
833   /*
834    * Make up the request packet
835    */
836   outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
837   if( f->callbacks->cilen && f->callbacks->addci ) {
838     cilen = (*f->callbacks->cilen)(f);
839     if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
840       cilen = peer_mru[f->unit] - HEADERLEN;
841     }
842     if (f->callbacks->addci) {
843       (*f->callbacks->addci)(f, outp, &cilen);
844     }
845   } else {
846     cilen = 0;
847   }
848 
849   /* send the request to our peer */
850   fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
851 
852   /* start the retransmit timer */
853   --f->retransmits;
854   TIMEOUT(fsm_timeout, f, f->timeouttime);
855 
856   FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
857         PROTO_NAME(f), f->reqid));
858 }
859 
860 
861 /*
862  * fsm_sdata - Send some data.
863  *
864  * Used for all packets sent to our peer by this module.
865  */
866 void
867 fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
868 {
869   u_char *outp;
870   int outlen;
871 
872   /* Adjust length to be smaller than MTU */
873   outp = outpacket_buf[f->unit];
874   if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
875     datalen = peer_mru[f->unit] - HEADERLEN;
876   }
877   if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
878     BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
879   }
880   outlen = datalen + HEADERLEN;
881   MAKEHEADER(outp, f->protocol);
882   PUTCHAR(code, outp);
883   PUTCHAR(id, outp);
884   PUTSHORT(outlen, outp);
885   pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
886   FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
887         PROTO_NAME(f), code, id, outlen));
888 }
889 
890 #endif /* PPP_SUPPORT */
891