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