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