fsm.c revision 31514
1/*
2 *		PPP Finite State Machine for LCP/IPCP
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: fsm.c,v 1.22 1997/11/22 03:37:31 brian Exp $
21 *
22 *  TODO:
23 *		o Refer loglevel for log output
24 *		o Better option log display
25 */
26#include <sys/param.h>
27#include <netinet/in.h>
28
29#include <stdio.h>
30#include <string.h>
31#include <termios.h>
32
33#include "command.h"
34#include "mbuf.h"
35#include "log.h"
36#include "defs.h"
37#include "timer.h"
38#include "fsm.h"
39#include "hdlc.h"
40#include "lqr.h"
41#include "lcpproto.h"
42#include "lcp.h"
43#include "ccp.h"
44#include "modem.h"
45#include "loadalias.h"
46#include "vars.h"
47
48u_char AckBuff[200];
49u_char NakBuff[200];
50u_char RejBuff[100];
51u_char ReqBuff[200];
52u_char *ackp = NULL;
53u_char *nakp = NULL;
54u_char *rejp = NULL;
55
56static void FsmSendConfigReq(struct fsm *);
57static void FsmSendTerminateReq(struct fsm *);
58static void FsmInitRestartCounter(struct fsm *);
59
60char const *StateNames[] = {
61  "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping",
62  "Req-Sent", "Ack-Rcvd", "Ack-Sent", "Opened",
63};
64
65static void
66StoppedTimeout(void *v)
67{
68  struct fsm *fp = (struct fsm *)v;
69
70  LogPrintf(fp->LogLevel, "Stopped timer expired\n");
71  if (modem != -1)
72    DownConnection();
73  else
74    FsmDown(fp);
75}
76
77void
78FsmInit(struct fsm * fp)
79{
80  LogPrintf(LogDEBUG, "FsmInit\n");
81  fp->state = ST_INITIAL;
82  fp->reqid = 1;
83  fp->restart = 1;
84  fp->maxconfig = 3;
85}
86
87static void
88NewState(struct fsm * fp, int new)
89{
90  LogPrintf(fp->LogLevel, "State change %s --> %s\n",
91	    StateNames[fp->state], StateNames[new]);
92  if (fp->state == ST_STOPPED && fp->StoppedTimer.state == TIMER_RUNNING)
93    StopTimer(&fp->StoppedTimer);
94  fp->state = new;
95  if ((new >= ST_INITIAL && new <= ST_STOPPED) || (new == ST_OPENED)) {
96    StopTimer(&fp->FsmTimer);
97    if (new == ST_STOPPED && fp->StoppedTimer.load) {
98      fp->StoppedTimer.state = TIMER_STOPPED;
99      fp->StoppedTimer.func = StoppedTimeout;
100      fp->StoppedTimer.arg = (void *) fp;
101      StartTimer(&fp->StoppedTimer);
102    }
103  }
104}
105
106void
107FsmOutput(struct fsm * fp, u_int code, u_int id, u_char * ptr, int count)
108{
109  int plen;
110  struct fsmheader lh;
111  struct mbuf *bp;
112
113  plen = sizeof(struct fsmheader) + count;
114  lh.code = code;
115  lh.id = id;
116  lh.length = htons(plen);
117  bp = mballoc(plen, MB_FSM);
118  memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
119  if (count)
120    memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
121  LogDumpBp(LogDEBUG, "FsmOutput", bp);
122  HdlcOutput(PRI_LINK, fp->proto, bp);
123}
124
125void
126FsmOpen(struct fsm * fp)
127{
128  switch (fp->state) {
129    case ST_INITIAL:
130    (fp->LayerStart) (fp);
131    NewState(fp, ST_STARTING);
132    break;
133  case ST_STARTING:
134    break;
135  case ST_CLOSED:
136    if (fp->open_mode == OPEN_PASSIVE) {
137      NewState(fp, ST_STOPPED);
138    } else {
139      FsmInitRestartCounter(fp);
140      FsmSendConfigReq(fp);
141      NewState(fp, ST_REQSENT);
142    }
143    break;
144  case ST_STOPPED:		/* XXX: restart option */
145  case ST_REQSENT:
146  case ST_ACKRCVD:
147  case ST_ACKSENT:
148  case ST_OPENED:		/* XXX: restart option */
149    break;
150  case ST_CLOSING:		/* XXX: restart option */
151  case ST_STOPPING:		/* XXX: restart option */
152    NewState(fp, ST_STOPPING);
153    break;
154  }
155}
156
157void
158FsmUp(struct fsm * fp)
159{
160  switch (fp->state) {
161    case ST_INITIAL:
162    NewState(fp, ST_CLOSED);
163    break;
164  case ST_STARTING:
165    FsmInitRestartCounter(fp);
166    FsmSendConfigReq(fp);
167    NewState(fp, ST_REQSENT);
168    break;
169  default:
170    LogPrintf(fp->LogLevel, "Oops, Up at %s\n", StateNames[fp->state]);
171    break;
172  }
173}
174
175void
176FsmDown(struct fsm * fp)
177{
178  switch (fp->state) {
179    case ST_CLOSED:
180    case ST_CLOSING:
181    NewState(fp, ST_INITIAL);
182    break;
183  case ST_STOPPED:
184    (fp->LayerStart) (fp);
185    /* Fall into.. */
186  case ST_STOPPING:
187  case ST_REQSENT:
188  case ST_ACKRCVD:
189  case ST_ACKSENT:
190    NewState(fp, ST_STARTING);
191    break;
192  case ST_OPENED:
193    (fp->LayerDown) (fp);
194    NewState(fp, ST_STARTING);
195    break;
196  }
197}
198
199void
200FsmClose(struct fsm * fp)
201{
202  switch (fp->state) {
203    case ST_STARTING:
204    NewState(fp, ST_INITIAL);
205    break;
206  case ST_STOPPED:
207    NewState(fp, ST_CLOSED);
208    break;
209  case ST_STOPPING:
210    NewState(fp, ST_CLOSING);
211    break;
212  case ST_OPENED:
213    (fp->LayerDown) (fp);
214    /* Fall down */
215  case ST_REQSENT:
216  case ST_ACKRCVD:
217  case ST_ACKSENT:
218    FsmInitRestartCounter(fp);
219    FsmSendTerminateReq(fp);
220    NewState(fp, ST_CLOSING);
221    break;
222  }
223}
224
225/*
226 *	Send functions
227 */
228static void
229FsmSendConfigReq(struct fsm * fp)
230{
231  if (--fp->maxconfig > 0) {
232    (fp->SendConfigReq) (fp);
233    StartTimer(&fp->FsmTimer);	/* Start restart timer */
234    fp->restart--;		/* Decrement restart counter */
235  } else {
236    FsmClose(fp);
237  }
238}
239
240static void
241FsmSendTerminateReq(struct fsm * fp)
242{
243  LogPrintf(fp->LogLevel, "SendTerminateReq.\n");
244  FsmOutput(fp, CODE_TERMREQ, fp->reqid++, NULL, 0);
245  (fp->SendTerminateReq) (fp);
246  StartTimer(&fp->FsmTimer);	/* Start restart timer */
247  fp->restart--;		/* Decrement restart counter */
248}
249
250static void
251FsmSendConfigAck(struct fsm * fp,
252		 struct fsmheader * lhp,
253		 u_char * option,
254		 int count)
255{
256  LogPrintf(fp->LogLevel, "SendConfigAck(%s)\n", StateNames[fp->state]);
257  (fp->DecodeConfig) (option, count, MODE_NOP);
258  FsmOutput(fp, CODE_CONFIGACK, lhp->id, option, count);
259}
260
261static void
262FsmSendConfigRej(struct fsm * fp,
263		 struct fsmheader * lhp,
264		 u_char * option,
265		 int count)
266{
267  LogPrintf(fp->LogLevel, "SendConfigRej(%s)\n", StateNames[fp->state]);
268  (fp->DecodeConfig) (option, count, MODE_NOP);
269  FsmOutput(fp, CODE_CONFIGREJ, lhp->id, option, count);
270}
271
272static void
273FsmSendConfigNak(struct fsm * fp,
274		 struct fsmheader * lhp,
275		 u_char * option,
276		 int count)
277{
278  LogPrintf(fp->LogLevel, "SendConfigNak(%s)\n", StateNames[fp->state]);
279  (fp->DecodeConfig) (option, count, MODE_NOP);
280  FsmOutput(fp, CODE_CONFIGNAK, lhp->id, option, count);
281}
282
283/*
284 *	Timeout actions
285 */
286static void
287FsmTimeout(void *v)
288{
289  struct fsm *fp = (struct fsm *)v;
290
291  if (fp->restart) {
292    switch (fp->state) {
293      case ST_CLOSING:
294      case ST_STOPPING:
295      FsmSendTerminateReq(fp);
296      break;
297    case ST_REQSENT:
298    case ST_ACKSENT:
299      FsmSendConfigReq(fp);
300      break;
301    case ST_ACKRCVD:
302      FsmSendConfigReq(fp);
303      NewState(fp, ST_REQSENT);
304      break;
305    }
306    StartTimer(&fp->FsmTimer);
307  } else {
308    switch (fp->state) {
309    case ST_CLOSING:
310      NewState(fp, ST_CLOSED);
311      (fp->LayerFinish) (fp);
312      break;
313    case ST_STOPPING:
314      NewState(fp, ST_STOPPED);
315      (fp->LayerFinish) (fp);
316      break;
317    case ST_REQSENT:		/* XXX: 3p */
318    case ST_ACKSENT:
319    case ST_ACKRCVD:
320      NewState(fp, ST_STOPPED);
321      (fp->LayerFinish) (fp);
322      break;
323    }
324  }
325}
326
327static void
328FsmInitRestartCounter(struct fsm * fp)
329{
330  StopTimer(&fp->FsmTimer);
331  fp->FsmTimer.state = TIMER_STOPPED;
332  fp->FsmTimer.func = FsmTimeout;
333  fp->FsmTimer.arg = (void *) fp;
334  (fp->InitRestartCounter) (fp);
335}
336
337/*
338 *   Actions when receive packets
339 */
340static void
341FsmRecvConfigReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
342/* RCR */
343{
344  int plen, flen;
345  int ackaction = 0;
346
347  plen = plength(bp);
348  flen = ntohs(lhp->length) - sizeof(*lhp);
349  if (plen < flen) {
350    LogPrintf(LogERROR, "FsmRecvConfigReq: plen (%d) < flen (%d)\n",
351	      plen, flen);
352    pfree(bp);
353    return;
354  }
355
356  /*
357   * Check and process easy case
358   */
359  switch (fp->state) {
360  case ST_INITIAL:
361  case ST_STARTING:
362    LogPrintf(fp->LogLevel, "Oops, RCR in %s.\n", StateNames[fp->state]);
363    pfree(bp);
364    return;
365  case ST_CLOSED:
366    (fp->SendTerminateAck) (fp);
367    pfree(bp);
368    return;
369  case ST_CLOSING:
370    LogPrintf(LogERROR, "Got ConfigReq while state = %d\n", fp->state);
371  case ST_STOPPING:
372    pfree(bp);
373    return;
374  }
375
376  (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_REQ);
377
378  if (nakp == NakBuff && rejp == RejBuff)
379    ackaction = 1;
380
381  switch (fp->state) {
382  case ST_OPENED:
383    (fp->LayerDown) (fp);
384    FsmSendConfigReq(fp);
385    break;
386  case ST_STOPPED:
387    FsmInitRestartCounter(fp);
388    FsmSendConfigReq(fp);
389    break;
390  }
391
392  if (rejp != RejBuff)
393    FsmSendConfigRej(fp, lhp, RejBuff, rejp - RejBuff);
394  if (nakp != NakBuff)
395    FsmSendConfigNak(fp, lhp, NakBuff, nakp - NakBuff);
396  if (ackaction)
397    FsmSendConfigAck(fp, lhp, AckBuff, ackp - AckBuff);
398
399  switch (fp->state) {
400  case ST_STOPPED:
401  case ST_OPENED:
402    if (ackaction)
403      NewState(fp, ST_ACKSENT);
404    else
405      NewState(fp, ST_REQSENT);
406    break;
407  case ST_REQSENT:
408    if (ackaction)
409      NewState(fp, ST_ACKSENT);
410    break;
411  case ST_ACKRCVD:
412    if (ackaction) {
413      NewState(fp, ST_OPENED);
414      (fp->LayerUp) (fp);
415    }
416    break;
417  case ST_ACKSENT:
418    if (!ackaction)
419      NewState(fp, ST_REQSENT);
420    break;
421  }
422  pfree(bp);
423}
424
425static void
426FsmRecvConfigAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
427/* RCA */
428{
429  switch (fp->state) {
430    case ST_CLOSED:
431    case ST_STOPPED:
432    (fp->SendTerminateAck) (fp);
433    break;
434  case ST_CLOSING:
435  case ST_STOPPING:
436    break;
437  case ST_REQSENT:
438    FsmInitRestartCounter(fp);
439    NewState(fp, ST_ACKRCVD);
440    break;
441  case ST_ACKRCVD:
442    FsmSendConfigReq(fp);
443    NewState(fp, ST_REQSENT);
444    break;
445  case ST_ACKSENT:
446    FsmInitRestartCounter(fp);
447    NewState(fp, ST_OPENED);
448    (fp->LayerUp) (fp);
449    break;
450  case ST_OPENED:
451    (fp->LayerDown) (fp);
452    FsmSendConfigReq(fp);
453    NewState(fp, ST_REQSENT);
454    break;
455  }
456  pfree(bp);
457}
458
459static void
460FsmRecvConfigNak(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
461/* RCN */
462{
463  int plen, flen;
464
465  plen = plength(bp);
466  flen = ntohs(lhp->length) - sizeof(*lhp);
467  if (plen < flen) {
468    pfree(bp);
469    return;
470  }
471
472  /*
473   * Check and process easy case
474   */
475  switch (fp->state) {
476  case ST_INITIAL:
477  case ST_STARTING:
478    LogPrintf(fp->LogLevel, "Oops, RCN in %s.\n", StateNames[fp->state]);
479    pfree(bp);
480    return;
481  case ST_CLOSED:
482  case ST_STOPPED:
483    (fp->SendTerminateAck) (fp);
484    pfree(bp);
485    return;
486  case ST_CLOSING:
487  case ST_STOPPING:
488    pfree(bp);
489    return;
490  }
491
492  (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_NAK);
493
494  switch (fp->state) {
495  case ST_REQSENT:
496  case ST_ACKSENT:
497    FsmInitRestartCounter(fp);
498    FsmSendConfigReq(fp);
499    break;
500  case ST_OPENED:
501    (fp->LayerDown) (fp);
502    /* Fall down */
503  case ST_ACKRCVD:
504    FsmSendConfigReq(fp);
505    NewState(fp, ST_REQSENT);
506    break;
507  }
508
509  pfree(bp);
510}
511
512static void
513FsmRecvTermReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
514/* RTR */
515{
516  switch (fp->state) {
517    case ST_INITIAL:
518    case ST_STARTING:
519    LogPrintf(fp->LogLevel, "Oops, RTR in %s\n", StateNames[fp->state]);
520    break;
521  case ST_CLOSED:
522  case ST_STOPPED:
523  case ST_CLOSING:
524  case ST_STOPPING:
525  case ST_REQSENT:
526    (fp->SendTerminateAck) (fp);
527    break;
528  case ST_ACKRCVD:
529  case ST_ACKSENT:
530    (fp->SendTerminateAck) (fp);
531    NewState(fp, ST_REQSENT);
532    break;
533  case ST_OPENED:
534    (fp->LayerDown) (fp);
535    (fp->SendTerminateAck) (fp);
536    StartTimer(&fp->FsmTimer);	/* Start restart timer */
537    fp->restart = 0;
538    NewState(fp, ST_STOPPING);
539    break;
540  }
541  pfree(bp);
542}
543
544static void
545FsmRecvTermAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
546/* RTA */
547{
548  switch (fp->state) {
549    case ST_CLOSING:
550    NewState(fp, ST_CLOSED);
551    (fp->LayerFinish) (fp);
552    break;
553  case ST_STOPPING:
554    NewState(fp, ST_STOPPED);
555    (fp->LayerFinish) (fp);
556    break;
557  case ST_ACKRCVD:
558    NewState(fp, ST_REQSENT);
559    break;
560  case ST_OPENED:
561    (fp->LayerDown) (fp);
562    FsmSendConfigReq(fp);
563    NewState(fp, ST_REQSENT);
564    break;
565  }
566  pfree(bp);
567}
568
569static void
570FsmRecvConfigRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
571/* RCJ */
572{
573  int plen, flen;
574
575  plen = plength(bp);
576  flen = ntohs(lhp->length) - sizeof(*lhp);
577  if (plen < flen) {
578    pfree(bp);
579    return;
580  }
581  LogPrintf(fp->LogLevel, "RecvConfigRej.\n");
582
583  /*
584   * Check and process easy case
585   */
586  switch (fp->state) {
587  case ST_INITIAL:
588  case ST_STARTING:
589    LogPrintf(fp->LogLevel, "Oops, RCJ in %s.\n", StateNames[fp->state]);
590    pfree(bp);
591    return;
592  case ST_CLOSED:
593  case ST_STOPPED:
594    (fp->SendTerminateAck) (fp);
595    pfree(bp);
596    return;
597  case ST_CLOSING:
598  case ST_STOPPING:
599    pfree(bp);
600    return;
601  }
602
603  (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_REJ);
604
605  switch (fp->state) {
606  case ST_REQSENT:
607  case ST_ACKSENT:
608    FsmInitRestartCounter(fp);
609    FsmSendConfigReq(fp);
610    break;
611  case ST_OPENED:
612    (fp->LayerDown) (fp);
613    /* Fall down */
614  case ST_ACKRCVD:
615    FsmSendConfigReq(fp);
616    NewState(fp, ST_REQSENT);
617    break;
618  }
619  pfree(bp);
620}
621
622static void
623FsmRecvCodeRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
624{
625  LogPrintf(fp->LogLevel, "RecvCodeRej\n");
626  pfree(bp);
627}
628
629static void
630FsmRecvProtoRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
631{
632  u_short *sp, proto;
633
634  sp = (u_short *) MBUF_CTOP(bp);
635  proto = ntohs(*sp);
636  LogPrintf(fp->LogLevel, "-- Protocol (%04x) was rejected.\n", proto);
637
638  switch (proto) {
639  case PROTO_LQR:
640    StopLqr(LQM_LQR);
641    break;
642  case PROTO_CCP:
643    fp = &CcpFsm;
644    (fp->LayerFinish) (fp);
645    switch (fp->state) {
646    case ST_CLOSED:
647    case ST_CLOSING:
648      NewState(fp, ST_CLOSED);
649    default:
650      NewState(fp, ST_STOPPED);
651      break;
652    }
653    break;
654  }
655  pfree(bp);
656}
657
658static void
659FsmRecvEchoReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
660{
661  u_char *cp;
662  u_long *lp, magic;
663
664  cp = MBUF_CTOP(bp);
665  lp = (u_long *) cp;
666  magic = ntohl(*lp);
667  if (magic != LcpInfo.his_magic) {
668    LogPrintf(LogERROR, "RecvEchoReq: his magic is bad!!\n");
669    /* XXX: We should send terminate request */
670  }
671  if (fp->state == ST_OPENED) {
672    *lp = htonl(LcpInfo.want_magic);	/* Insert local magic number */
673    LogPrintf(fp->LogLevel, "SendEchoRep(%s)\n", StateNames[fp->state]);
674    FsmOutput(fp, CODE_ECHOREP, lhp->id, cp, plength(bp));
675  }
676  pfree(bp);
677}
678
679static void
680FsmRecvEchoRep(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
681{
682  u_long *lp, magic;
683
684  lp = (u_long *) MBUF_CTOP(bp);
685  magic = ntohl(*lp);
686/*
687 * Tolerate echo replies with either magic number
688 */
689  if (magic != 0 && magic != LcpInfo.his_magic && magic != LcpInfo.want_magic) {
690    LogPrintf(LogERROR, "RecvEchoRep: his magic is wrong! expect: %x got: %x\n",
691	      LcpInfo.his_magic, magic);
692
693    /*
694     * XXX: We should send terminate request. But poor implementation may die
695     * as a result.
696     */
697  }
698  RecvEchoLqr(bp);
699  pfree(bp);
700}
701
702static void
703FsmRecvDiscReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
704{
705  LogPrintf(fp->LogLevel, "RecvDiscReq\n");
706  pfree(bp);
707}
708
709static void
710FsmRecvIdent(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
711{
712  LogPrintf(fp->LogLevel, "RecvIdent\n");
713  pfree(bp);
714}
715
716static void
717FsmRecvTimeRemain(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
718{
719  LogPrintf(fp->LogLevel, "RecvTimeRemain\n");
720  pfree(bp);
721}
722
723static void
724FsmRecvResetReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
725{
726  LogPrintf(fp->LogLevel, "RecvResetReq\n");
727  CcpRecvResetReq(fp);
728  LogPrintf(fp->LogLevel, "SendResetAck\n");
729  FsmOutput(fp, CODE_RESETACK, fp->reqid, NULL, 0);
730  pfree(bp);
731}
732
733static void
734FsmRecvResetAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
735{
736  LogPrintf(fp->LogLevel, "RecvResetAck\n");
737  CcpResetInput();
738  fp->reqid++;
739  pfree(bp);
740}
741
742static const struct fsmcodedesc FsmCodes[] = {
743  {FsmRecvConfigReq, "Configure Request",},
744  {FsmRecvConfigAck, "Configure Ack",},
745  {FsmRecvConfigNak, "Configure Nak",},
746  {FsmRecvConfigRej, "Configure Reject",},
747  {FsmRecvTermReq, "Terminate Request",},
748  {FsmRecvTermAck, "Terminate Ack",},
749  {FsmRecvCodeRej, "Code Reject",},
750  {FsmRecvProtoRej, "Protocol Reject",},
751  {FsmRecvEchoReq, "Echo Request",},
752  {FsmRecvEchoRep, "Echo Reply",},
753  {FsmRecvDiscReq, "Discard Request",},
754  {FsmRecvIdent, "Ident",},
755  {FsmRecvTimeRemain, "Time Remain",},
756  {FsmRecvResetReq, "Reset Request",},
757  {FsmRecvResetAck, "Reset Ack",},
758};
759
760void
761FsmInput(struct fsm * fp, struct mbuf * bp)
762{
763  int len;
764  struct fsmheader *lhp;
765  const struct fsmcodedesc *codep;
766
767  len = plength(bp);
768  if (len < sizeof(struct fsmheader)) {
769    pfree(bp);
770    return;
771  }
772  lhp = (struct fsmheader *) MBUF_CTOP(bp);
773  if (lhp->code == 0 || lhp->code > fp->max_code) {
774    pfree(bp);			/* XXX: Should send code reject */
775    return;
776  }
777  bp->offset += sizeof(struct fsmheader);
778  bp->cnt -= sizeof(struct fsmheader);
779
780  codep = FsmCodes + lhp->code - 1;
781  LogPrintf(fp->LogLevel, "Received %s (%d) state = %s (%d)\n",
782	    codep->name, lhp->id, StateNames[fp->state], fp->state);
783  if (LogIsKept(LogDEBUG))
784    LogMemory();
785  (codep->action) (fp, lhp, bp);
786  if (LogIsKept(LogDEBUG))
787    LogMemory();
788}
789