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