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