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