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