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