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