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