First run at integrating LWIP into the tree (again)
[akaros.git] / user / lwip / netif / ppp / fsm.c
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.h"
63 #include "pppdebug.h"
64
65 #include "fsm.h"
66
67 #include <string.h>
68
69
70 /*************************/
71 /*** LOCAL DEFINITIONS ***/
72 /*************************/
73
74 #if PPP_DEBUG
75
76 static const char *ppperr_strerr[] = {
77            "LS_INITIAL",  /* LS_INITIAL  0 */
78            "LS_STARTING", /* LS_STARTING 1 */
79            "LS_CLOSED",   /* LS_CLOSED   2 */
80            "LS_STOPPED",  /* LS_STOPPED  3 */
81            "LS_CLOSING",  /* LS_CLOSING  4 */
82            "LS_STOPPING", /* LS_STOPPING 5 */
83            "LS_REQSENT",  /* LS_REQSENT  6 */
84            "LS_ACKRCVD",  /* LS_ACKRCVD  7 */
85            "LS_ACKSENT",  /* LS_ACKSENT  8 */
86            "LS_OPENED"    /* LS_OPENED   9 */
87 };
88
89 #endif /* PPP_DEBUG */
90
91 /************************/
92 /*** LOCAL DATA TYPES ***/
93 /************************/
94
95
96 /***********************************/
97 /*** LOCAL FUNCTION DECLARATIONS ***/
98 /***********************************/
99 static void fsm_timeout (void *);
100 static void fsm_rconfreq (fsm *, u_char, u_char *, int);
101 static void fsm_rconfack (fsm *, int, u_char *, int);
102 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
103 static void fsm_rtermreq (fsm *, int, u_char *, int);
104 static void fsm_rtermack (fsm *);
105 static void fsm_rcoderej (fsm *, u_char *, int);
106 static void fsm_sconfreq (fsm *, int);
107
108 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
109
110
111 /******************************/
112 /*** PUBLIC DATA STRUCTURES ***/
113 /******************************/
114
115
116 /*****************************/
117 /*** LOCAL DATA STRUCTURES ***/
118 /*****************************/
119 int peer_mru[NUM_PPP];
120
121
122 /***********************************/
123 /*** PUBLIC FUNCTION DEFINITIONS ***/
124 /***********************************/
125
126 /*
127  * fsm_init - Initialize fsm.
128  *
129  * Initialize fsm state.
130  */
131 void
132 fsm_init(fsm *f)
133 {
134   f->state = LS_INITIAL;
135   f->flags = 0;
136   f->id = 0;        /* XXX Start with random id? */
137   f->timeouttime = FSM_DEFTIMEOUT;
138   f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
139   f->maxtermtransmits = FSM_DEFMAXTERMREQS;
140   f->maxnakloops = FSM_DEFMAXNAKLOOPS;
141   f->term_reason_len = 0;
142 }
143
144
145 /*
146  * fsm_lowerup - The lower layer is up.
147  */
148 void
149 fsm_lowerup(fsm *f)
150 {
151   int oldState = f->state;
152
153   LWIP_UNUSED_ARG(oldState);
154
155   switch( f->state ) {
156     case LS_INITIAL:
157       f->state = LS_CLOSED;
158       break;
159
160     case LS_STARTING:
161       if( f->flags & OPT_SILENT ) {
162         f->state = LS_STOPPED;
163       } else {
164         /* Send an initial configure-request */
165         fsm_sconfreq(f, 0);
166         f->state = LS_REQSENT;
167       }
168     break;
169
170     default:
171       FSMDEBUG((LOG_INFO, "%s: Up event in state %d (%s)!\n",
172           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
173   }
174   
175   FSMDEBUG((LOG_INFO, "%s: lowerup state %d (%s) -> %d (%s)\n",
176       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
177 }
178
179
180 /*
181  * fsm_lowerdown - The lower layer is down.
182  *
183  * Cancel all timeouts and inform upper layers.
184  */
185 void
186 fsm_lowerdown(fsm *f)
187 {
188   int oldState = f->state;
189
190   LWIP_UNUSED_ARG(oldState);
191
192   switch( f->state ) {
193     case LS_CLOSED:
194       f->state = LS_INITIAL;
195       break;
196
197     case LS_STOPPED:
198       f->state = LS_STARTING;
199       if( f->callbacks->starting ) {
200         (*f->callbacks->starting)(f);
201       }
202       break;
203
204     case LS_CLOSING:
205       f->state = LS_INITIAL;
206       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
207       break;
208
209     case LS_STOPPING:
210     case LS_REQSENT:
211     case LS_ACKRCVD:
212     case LS_ACKSENT:
213       f->state = LS_STARTING;
214       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
215       break;
216
217     case LS_OPENED:
218       if( f->callbacks->down ) {
219         (*f->callbacks->down)(f);
220       }
221       f->state = LS_STARTING;
222       break;
223
224     default:
225       FSMDEBUG((LOG_INFO, "%s: Down event in state %d (%s)!\n",
226           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
227   }
228
229   FSMDEBUG((LOG_INFO, "%s: lowerdown state %d (%s) -> %d (%s)\n",
230       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
231 }
232
233
234 /*
235  * fsm_open - Link is allowed to come up.
236  */
237 void
238 fsm_open(fsm *f)
239 {
240   int oldState = f->state;
241
242   LWIP_UNUSED_ARG(oldState);
243
244   switch( f->state ) {
245     case LS_INITIAL:
246       f->state = LS_STARTING;
247       if( f->callbacks->starting ) {
248         (*f->callbacks->starting)(f);
249       }
250       break;
251
252     case LS_CLOSED:
253       if( f->flags & OPT_SILENT ) {
254         f->state = LS_STOPPED;
255       } else {
256         /* Send an initial configure-request */
257         fsm_sconfreq(f, 0);
258         f->state = LS_REQSENT;
259       }
260       break;
261   
262     case LS_CLOSING:
263       f->state = LS_STOPPING;
264       /* fall through */
265     case LS_STOPPED:
266     case LS_OPENED:
267       if( f->flags & OPT_RESTART ) {
268         fsm_lowerdown(f);
269         fsm_lowerup(f);
270       }
271       break;
272   }
273
274   FSMDEBUG((LOG_INFO, "%s: open state %d (%s) -> %d (%s)\n",
275       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
276 }
277
278
279 /*
280  * fsm_close - Start closing connection.
281  *
282  * Cancel timeouts and either initiate close or possibly go directly to
283  * the LS_CLOSED state.
284  */
285 void
286 fsm_close(fsm *f, char *reason)
287 {
288   int oldState = f->state;
289
290   LWIP_UNUSED_ARG(oldState);
291
292   f->term_reason = reason;
293   f->term_reason_len = (reason == NULL? 0: strlen(reason));
294   switch( f->state ) {
295     case LS_STARTING:
296       f->state = LS_INITIAL;
297       break;
298     case LS_STOPPED:
299       f->state = LS_CLOSED;
300       break;
301     case LS_STOPPING:
302       f->state = LS_CLOSING;
303       break;
304
305     case LS_REQSENT:
306     case LS_ACKRCVD:
307     case LS_ACKSENT:
308     case LS_OPENED:
309       if( f->state != LS_OPENED ) {
310         UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
311       } else if( f->callbacks->down ) {
312         (*f->callbacks->down)(f);  /* Inform upper layers we're down */
313       }
314       /* Init restart counter, send Terminate-Request */
315       f->retransmits = f->maxtermtransmits;
316       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
317             (u_char *) f->term_reason, f->term_reason_len);
318       TIMEOUT(fsm_timeout, f, f->timeouttime);
319       --f->retransmits;
320
321       f->state = LS_CLOSING;
322       break;
323   }
324
325   FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d (%s) -> %d (%s)\n",
326       PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
327 }
328
329
330 /*
331  * fsm_sdata - Send some data.
332  *
333  * Used for all packets sent to our peer by this module.
334  */
335 void
336 fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
337 {
338   u_char *outp;
339   int outlen;
340
341   /* Adjust length to be smaller than MTU */
342   outp = outpacket_buf[f->unit];
343   if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
344     datalen = peer_mru[f->unit] - HEADERLEN;
345   }
346   if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
347     BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
348   }
349   outlen = datalen + HEADERLEN;
350   MAKEHEADER(outp, f->protocol);
351   PUTCHAR(code, outp);
352   PUTCHAR(id, outp);
353   PUTSHORT(outlen, outp);
354   pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
355   FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n",
356         PROTO_NAME(f), code, id, outlen));
357 }
358
359
360 /*
361  * fsm_input - Input packet.
362  */
363 void
364 fsm_input(fsm *f, u_char *inpacket, int l)
365 {
366   u_char *inp = inpacket;
367   u_char code, id;
368   int len;
369
370   /*
371   * Parse header (code, id and length).
372   * If packet too short, drop it.
373   */
374   if (l < HEADERLEN) {
375     FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n",
376           f->protocol));
377     return;
378   }
379   GETCHAR(code, inp);
380   GETCHAR(id, inp);
381   GETSHORT(len, inp);
382   if (len < HEADERLEN) {
383     FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n",
384         f->protocol));
385     return;
386   }
387   if (len > l) {
388     FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n",
389         f->protocol));
390     return;
391   }
392   len -= HEADERLEN;    /* subtract header length */
393
394   if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
395     FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d (%s).\n",
396         f->protocol, f->state, ppperr_strerr[f->state]));
397     return;
398   }
399   FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
400   /*
401    * Action depends on code.
402    */
403   switch (code) {
404     case CONFREQ:
405       fsm_rconfreq(f, id, inp, len);
406       break;
407     
408     case CONFACK:
409       fsm_rconfack(f, id, inp, len);
410       break;
411     
412     case CONFNAK:
413     case CONFREJ:
414       fsm_rconfnakrej(f, code, id, inp, len);
415       break;
416     
417     case TERMREQ:
418       fsm_rtermreq(f, id, inp, len);
419       break;
420     
421     case TERMACK:
422       fsm_rtermack(f);
423       break;
424     
425     case CODEREJ:
426       fsm_rcoderej(f, inp, len);
427       break;
428     
429     default:
430       if( !f->callbacks->extcode ||
431           !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
432         fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
433       }
434       break;
435   }
436 }
437
438
439 /*
440  * fsm_protreject - Peer doesn't speak this protocol.
441  *
442  * Treat this as a catastrophic error (RXJ-).
443  */
444 void
445 fsm_protreject(fsm *f)
446 {
447   switch( f->state ) {
448     case LS_CLOSING:
449       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
450       /* fall through */
451     case LS_CLOSED:
452       f->state = LS_CLOSED;
453       if( f->callbacks->finished ) {
454         (*f->callbacks->finished)(f);
455       }
456       break;
457
458     case LS_STOPPING:
459     case LS_REQSENT:
460     case LS_ACKRCVD:
461     case LS_ACKSENT:
462       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
463       /* fall through */
464     case LS_STOPPED:
465       f->state = LS_STOPPED;
466       if( f->callbacks->finished ) {
467         (*f->callbacks->finished)(f);
468       }
469       break;
470     
471     case LS_OPENED:
472       if( f->callbacks->down ) {
473         (*f->callbacks->down)(f);
474       }
475       /* Init restart counter, send Terminate-Request */
476       f->retransmits = f->maxtermtransmits;
477       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
478             (u_char *) f->term_reason, f->term_reason_len);
479       TIMEOUT(fsm_timeout, f, f->timeouttime);
480       --f->retransmits;
481       
482       f->state = LS_STOPPING;
483       break;
484     
485     default:
486       FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d (%s)!\n",
487             PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
488     }
489 }
490
491
492
493
494
495 /**********************************/
496 /*** LOCAL FUNCTION DEFINITIONS ***/
497 /**********************************/
498
499 /*
500  * fsm_timeout - Timeout expired.
501  */
502 static void
503 fsm_timeout(void *arg)
504 {
505   fsm *f = (fsm *) arg;
506
507   switch (f->state) {
508     case LS_CLOSING:
509     case LS_STOPPING:
510       if( f->retransmits <= 0 ) {
511         FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d (%s)\n",
512              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
513         /*
514          * We've waited for an ack long enough.  Peer probably heard us.
515          */
516         f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
517         if( f->callbacks->finished ) {
518           (*f->callbacks->finished)(f);
519         }
520       } else {
521         FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d (%s)\n",
522              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
523         /* Send Terminate-Request */
524         fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
525             (u_char *) f->term_reason, f->term_reason_len);
526         TIMEOUT(fsm_timeout, f, f->timeouttime);
527         --f->retransmits;
528       }
529       break;
530
531     case LS_REQSENT:
532     case LS_ACKRCVD:
533     case LS_ACKSENT:
534       if (f->retransmits <= 0) {
535         FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d (%s)\n",
536          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
537         f->state = LS_STOPPED;
538         if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
539           (*f->callbacks->finished)(f);
540         }
541       } else {
542         FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d (%s)\n",
543          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
544         /* Retransmit the configure-request */
545         if (f->callbacks->retransmit) {
546           (*f->callbacks->retransmit)(f);
547         }
548         fsm_sconfreq(f, 1);    /* Re-send Configure-Request */
549         if( f->state == LS_ACKRCVD ) {
550           f->state = LS_REQSENT;
551         }
552       }
553       break;
554
555     default:
556       FSMDEBUG((LOG_INFO, "%s: UNHANDLED timeout event in state %d (%s)!\n",
557           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
558   }
559 }
560
561
562 /*
563  * fsm_rconfreq - Receive Configure-Request.
564  */
565 static void
566 fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
567 {
568   int code, reject_if_disagree;
569
570   FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", 
571         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
572   switch( f->state ) {
573     case LS_CLOSED:
574       /* Go away, we're closed */
575       fsm_sdata(f, TERMACK, id, NULL, 0);
576       return;
577     case LS_CLOSING:
578     case LS_STOPPING:
579       return;
580
581     case LS_OPENED:
582       /* Go down and restart negotiation */
583       if( f->callbacks->down ) {
584         (*f->callbacks->down)(f);  /* Inform upper layers */
585       }
586       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
587       break;
588
589     case LS_STOPPED:
590       /* Negotiation started by our peer */
591       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
592       f->state = LS_REQSENT;
593       break;
594   }
595   
596   /*
597   * Pass the requested configuration options
598   * to protocol-specific code for checking.
599   */
600   if (f->callbacks->reqci) {    /* Check CI */
601     reject_if_disagree = (f->nakloops >= f->maxnakloops);
602     code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
603   } else if (len) {
604     code = CONFREJ;      /* Reject all CI */
605   } else {
606     code = CONFACK;
607   }
608   
609   /* send the Ack, Nak or Rej to the peer */
610   fsm_sdata(f, (u_char)code, id, inp, len);
611   
612   if (code == CONFACK) {
613     if (f->state == LS_ACKRCVD) {
614       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
615       f->state = LS_OPENED;
616       if (f->callbacks->up) {
617         (*f->callbacks->up)(f);  /* Inform upper layers */
618       }
619     } else {
620       f->state = LS_ACKSENT;
621     }
622     f->nakloops = 0;
623   } else {
624     /* we sent CONFACK or CONFREJ */
625     if (f->state != LS_ACKRCVD) {
626       f->state = LS_REQSENT;
627     }
628     if( code == CONFNAK ) {
629       ++f->nakloops;
630     }
631   }
632 }
633
634
635 /*
636  * fsm_rconfack - Receive Configure-Ack.
637  */
638 static void
639 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
640 {
641   FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
642         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
643   
644   if (id != f->reqid || f->seen_ack) {   /* Expected id? */
645     return; /* Nope, toss... */
646   }
647   if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
648     /* Ack is bad - ignore it */
649     FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n",
650           PROTO_NAME(f), len));
651     return;
652   }
653   f->seen_ack = 1;
654   
655   switch (f->state) {
656     case LS_CLOSED:
657     case LS_STOPPED:
658       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
659       break;
660     
661     case LS_REQSENT:
662       f->state = LS_ACKRCVD;
663       f->retransmits = f->maxconfreqtransmits;
664       break;
665     
666     case LS_ACKRCVD:
667       /* Huh? an extra valid Ack? oh well... */
668       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
669       fsm_sconfreq(f, 0);
670       f->state = LS_REQSENT;
671       break;
672     
673     case LS_ACKSENT:
674       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
675       f->state = LS_OPENED;
676       f->retransmits = f->maxconfreqtransmits;
677       if (f->callbacks->up) {
678         (*f->callbacks->up)(f);  /* Inform upper layers */
679       }
680       break;
681     
682     case LS_OPENED:
683       /* Go down and restart negotiation */
684       if (f->callbacks->down) {
685         (*f->callbacks->down)(f);  /* Inform upper layers */
686       }
687       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
688       f->state = LS_REQSENT;
689       break;
690   }
691 }
692
693
694 /*
695  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
696  */
697 static void
698 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
699 {
700   int (*proc) (fsm *, u_char *, int);
701   int ret;
702
703   FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
704         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
705
706   if (id != f->reqid || f->seen_ack) { /* Expected id? */
707     return;        /* Nope, toss... */
708   }
709   proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
710   if (!proc || !((ret = proc(f, inp, len)))) {
711     /* Nak/reject is bad - ignore it */
712     FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n",
713           PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
714     return;
715   }
716   f->seen_ack = 1;
717
718   switch (f->state) {
719     case LS_CLOSED:
720     case LS_STOPPED:
721       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
722       break;
723     
724     case LS_REQSENT:
725     case LS_ACKSENT:
726       /* They didn't agree to what we wanted - try another request */
727       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
728       if (ret < 0) {
729         f->state = LS_STOPPED;    /* kludge for stopping CCP */
730       } else {
731         fsm_sconfreq(f, 0);    /* Send Configure-Request */
732       }
733       break;
734     
735     case LS_ACKRCVD:
736       /* Got a Nak/reject when we had already had an Ack?? oh well... */
737       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
738       fsm_sconfreq(f, 0);
739       f->state = LS_REQSENT;
740       break;
741     
742     case LS_OPENED:
743       /* Go down and restart negotiation */
744       if (f->callbacks->down) {
745         (*f->callbacks->down)(f);  /* Inform upper layers */
746       }
747       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
748       f->state = LS_REQSENT;
749       break;
750   }
751 }
752
753
754 /*
755  * fsm_rtermreq - Receive Terminate-Req.
756  */
757 static void
758 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
759 {
760   LWIP_UNUSED_ARG(p);
761
762   FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
763         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
764
765   switch (f->state) {
766     case LS_ACKRCVD:
767     case LS_ACKSENT:
768       f->state = LS_REQSENT;    /* Start over but keep trying */
769       break;
770
771     case LS_OPENED:
772       if (len > 0) {
773         FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p));
774       } else {
775         FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f)));
776       }
777       if (f->callbacks->down) {
778         (*f->callbacks->down)(f);  /* Inform upper layers */
779       }
780       f->retransmits = 0;
781       f->state = LS_STOPPING;
782       TIMEOUT(fsm_timeout, f, f->timeouttime);
783       break;
784   }
785
786   fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
787 }
788
789
790 /*
791  * fsm_rtermack - Receive Terminate-Ack.
792  */
793 static void
794 fsm_rtermack(fsm *f)
795 {
796   FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d (%s)\n", 
797         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
798   
799   switch (f->state) {
800     case LS_CLOSING:
801       UNTIMEOUT(fsm_timeout, f);
802       f->state = LS_CLOSED;
803       if( f->callbacks->finished ) {
804         (*f->callbacks->finished)(f);
805       }
806       break;
807
808     case LS_STOPPING:
809       UNTIMEOUT(fsm_timeout, f);
810       f->state = LS_STOPPED;
811       if( f->callbacks->finished ) {
812         (*f->callbacks->finished)(f);
813       }
814       break;
815     
816     case LS_ACKRCVD:
817       f->state = LS_REQSENT;
818       break;
819     
820     case LS_OPENED:
821       if (f->callbacks->down) {
822         (*f->callbacks->down)(f);  /* Inform upper layers */
823       }
824       fsm_sconfreq(f, 0);
825       break;
826   }
827 }
828
829
830 /*
831  * fsm_rcoderej - Receive an Code-Reject.
832  */
833 static void
834 fsm_rcoderej(fsm *f, u_char *inp, int len)
835 {
836   u_char code, id;
837   
838   FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d (%s)\n", 
839         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
840   
841   if (len < HEADERLEN) {
842     FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
843     return;
844   }
845   GETCHAR(code, inp);
846   GETCHAR(id, inp);
847   FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n",
848         PROTO_NAME(f), code, id));
849   
850   if( f->state == LS_ACKRCVD ) {
851     f->state = LS_REQSENT;
852   }
853 }
854
855
856 /*
857  * fsm_sconfreq - Send a Configure-Request.
858  */
859 static void
860 fsm_sconfreq(fsm *f, int retransmit)
861 {
862   u_char *outp;
863   int cilen;
864   
865   if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
866     /* Not currently negotiating - reset options */
867     if( f->callbacks->resetci ) {
868       (*f->callbacks->resetci)(f);
869     }
870     f->nakloops = 0;
871   }
872   
873   if( !retransmit ) {
874     /* New request - reset retransmission counter, use new ID */
875     f->retransmits = f->maxconfreqtransmits;
876     f->reqid = ++f->id;
877   }
878   
879   f->seen_ack = 0;
880   
881   /*
882    * Make up the request packet
883    */
884   outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
885   if( f->callbacks->cilen && f->callbacks->addci ) {
886     cilen = (*f->callbacks->cilen)(f);
887     if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
888       cilen = peer_mru[f->unit] - HEADERLEN;
889     }
890     if (f->callbacks->addci) {
891       (*f->callbacks->addci)(f, outp, &cilen);
892     }
893   } else {
894     cilen = 0;
895   }
896
897   /* send the request to our peer */
898   fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
899   
900   /* start the retransmit timer */
901   --f->retransmits;
902   TIMEOUT(fsm_timeout, f, f->timeouttime);
903   
904   FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n",
905         PROTO_NAME(f), f->reqid));
906 }
907
908 #endif /* PPP_SUPPORT */