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