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