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