This is fsm.c in view mode; [Download] [Up]
/* * fsm.c - {Link, IP} Control Protocol Finite State Machine. * * Copyright (c) 1989 Carnegie Mellon University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Carnegie Mellon University. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * TODO: * Mechanism to exit() and/or drop DTR. * Hold-down on open? * Randomize fsm id on link/init. * Deal with variable outgoing MTU. */ #include <stdio.h> #include <sys/types.h> #ifdef STREAMS #include <sys/stream.h> #include <sys/socket.h> #include <net/if.h> #endif #include "ppp.h" #include "fsm.h" void fsm_timeout(), fsm_rconfreq(), fsm_rconfack(), fsm_rconfnak(); void fsm_rconfrej(), fsm_rtermreq(), fsm_rtermack(), fsm_rcoderej(); void fsm_rprotrej(), fsm_rechoreq(), fsm_sconfreq(), fsm_sdata(); /* * fsm_init - Initialize fsm. * * Initialize fsm state. */ void fsm_init(f) fsm *f; { f->state = CLOSED; f->flags = 0; f->id = 0; /* XXX Start with random id? */ } /* * fsm_activeopen - Actively open connection. * * Set new state, reset desired options and send requests. */ void fsm_activeopen(f) fsm *f; { f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ if (f->state == REQSENT || /* Already actively open(ing)? */ f->state == ACKRCVD || f->state == ACKSENT || f->state == OPEN) return; if (f->state == TERMSENT || /* Closing or */ !(f->flags & LOWERUP)) { /* lower layer down? */ f->flags |= AOPENDING; /* Wait for desired event */ return; } if (f->callbacks->resetci) (*f->callbacks->resetci)(f); /* Reset options */ fsm_sconfreq(f); /* Send Configure-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); f->state = REQSENT; f->retransmits = 0; /* Reset retransmits count */ f->nakloops = 0; /* Reset nakloops count */ } /* * fsm_passiveopen - Passively open connection. * * Set new state and reset desired options. */ void fsm_passiveopen(f) fsm *f; { f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ if (f->state == LISTEN || /* Already passively open(ing)? */ f->state == OPEN) return; if (f->state == REQSENT || /* Active-Opening or */ f->state == ACKRCVD || f->state == ACKSENT || f->state == TERMSENT || /* closing or */ !(f->flags & LOWERUP)) { /* lower layer down? */ f->flags |= POPENDING; /* Wait for desired event */ return; } if (f->callbacks->resetci) (*f->callbacks->resetci)(f); /* Reset options */ f->state = LISTEN; f->retransmits = 0; /* Reset retransmits count */ f->nakloops = 0; /* Reset nakloops count */ } /* * fsm_close - Start closing connection. * * Cancel timeouts and either initiate close or possibly go directly to * the CLOSED state. */ void fsm_close(f) fsm *f; { f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */ if (f->state == CLOSED || /* Already CLOSED or Closing? */ f->state == TERMSENT) return; if (f->state == REQSENT || /* Timeout pending for Open? */ f->state == ACKRCVD || f->state == ACKSENT) UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->state == OPEN && /* Open? */ f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers we're down */ if (f->state == ACKSENT || /* Could peer be OPEN? */ f->state == OPEN) { fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); /* Send Terminate-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); f->state = TERMSENT; f->retransmits = 0; /* Reset retransmits count */ } else { f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ } } /* * fsm_timeout - Timeout expired. */ void fsm_timeout(f) fsm *f; { switch (f->state) { case REQSENT: case ACKRCVD: case ACKSENT: if (f->flags & POPENDING) { /* Go passive? */ f->state = CLOSED; /* Pretend for a moment... */ fsm_passiveopen(f); return; } if (f->callbacks->retransmit) /* If there is a retransmit rtn? */ (*f->callbacks->retransmit)(f); fsm_sconfreq(f); /* Send Configure-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); f->state = REQSENT; ++f->retransmits; f->nakloops = 0; break; case TERMSENT: if (f->flags & POPENDING) { /* Go passive? */ f->state = CLOSED; /* Pretend for a moment... */ fsm_passiveopen(f); return; } if (++f->retransmits > f->maxtermtransmits) { /* * We've waited for an ack long enough. Peer probably heard us. */ f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ return; } if (f->callbacks->retransmit) /* If there is a retransmit rtn? */ (*f->callbacks->retransmit)(f); fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0); /* Send Terminate-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); ++f->retransmits; } } /* * fsm_lowerup - The lower layer is up. * * Start Active or Passive Open if pending. */ void fsm_lowerup(f) fsm *f; { f->flags |= LOWERUP; if (f->flags & AOPENDING) /* Attempting Active-Open? */ fsm_activeopen(f); /* Try it now */ else if (f->flags & POPENDING) /* Attempting Passive-Open? */ fsm_passiveopen(f); /* Try it now */ } /* * fsm_lowerdown - The lower layer is down. * * Cancel all timeouts and inform upper layers. */ void fsm_lowerdown(f) fsm *f; { #if 0 /* XXX */ f->flags &= ~(LOWERUP|AOPENDING|POPENDING); /* Clear pending flags */ #endif f->flags &= ~LOWERUP; if (f->state == REQSENT || /* Timeout pending? */ f->state == ACKRCVD || f->state == ACKSENT || f->state == TERMSENT) UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->state == OPEN && /* OPEN? */ f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ } /* * fsm_protreject - Peer doesn't speak this protocol. * * Pretend that the lower layer went down. */ void fsm_protreject(f) fsm *f; { fsm_lowerdown(f); } /* * fsm_input - Input packet. */ void fsm_input(f, inpacket, l) fsm *f; PACKET *inpacket; int l; { u_char *inp; u_char code, id; int len; /* * Parse header (code, id and length). * If packet too short, drop it. */ inp = PACKET_DATA(inpacket); if (l < HEADERLEN) { FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd short header.\n", f->protocol)); goto freepacket; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < HEADERLEN) { FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd illegal length.\n", f->protocol)); goto freepacket; } if (len > l) { FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd short packet.\n", f->protocol)); goto freepacket; } len -= HEADERLEN; /* * Action depends on code. */ switch (code) { case CONFREQ: FSMDEBUG((stderr, "ppp: fsm_rconfreq(%x): Rcvd id %d.\n", f->protocol, id)); if (f->state == TERMSENT) goto freepacket; if (f->state == CLOSED) { fsm_sdata(f, TERMACK, id, NULL, 0); goto freepacket; } if (f->state == OPEN && f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ if (f->state == OPEN || f->state == LISTEN) { /* XXX Possibly need hold-down on OPEN? */ fsm_sconfreq(f); /* Send Configure-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); } if (f->callbacks->reqci) /* Check CI */ code = (*f->callbacks->reqci)(f, inp, &len); else if (len) code = CONFREJ; /* Reject all CI */ inp = PACKET_DATA(inpacket); /* Reset to header */ PUTCHAR(code, inp); PUTCHAR(id, inp); len += HEADERLEN; PUTSHORT(len, inp); OUTPUT(f->unit, inpacket, len, f->protocol); if (code == CONFACK) { if (f->state == ACKRCVD) { UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->callbacks->up) (*f->callbacks->up)(f); /* Inform upper layers */ f->state = OPEN; } else f->state = ACKSENT; } else { if (f->state != ACKRCVD) f->state = REQSENT; } return; case CONFACK: fsm_rconfack(f, inp, id, len); break; case CONFNAK: fsm_rconfnak(f, inp, id, len); break; case CONFREJ: fsm_rconfrej(f, inp, id, len); break; case TERMREQ: fsm_rtermreq(f, id); break; case TERMACK: fsm_rtermack(f); break; case CODEREJ: fsm_rcoderej(f, inp, len); break; case PROTREJ: fsm_rprotrej(f, inp, len); break; case ECHOREQ: FSMDEBUG((stderr, "ppp: fsm_rechoreq(%x): Rcvd id %d.\n", f->protocol, id)); switch (f->state) { case CLOSED: case LISTEN: fsm_sdata(f, TERMACK, id, NULL, 0); break; case OPEN: inp = PACKET_DATA(inpacket); /* Reset to header */ PUTCHAR(ECHOREP, inp); PUTCHAR(id, inp); len += HEADERLEN; PUTSHORT(len, inp); OUTPUT(f->unit, inpacket, len, f->protocol); return; } break; case ECHOREP: case DISCREQ: /* XXX Deliver to ECHOREQ sender? */ break; default: fsm_sdata(f, CODEREJ, ++f->id, PACKET_DATA(inpacket), len + HEADERLEN); break; } freepacket: PACKET_FREE(inpacket); } /* * fsm_rconfack - Receive Configure-Ack. */ void fsm_rconfack(f, inp, id, len) fsm *f; u_char *inp; u_char id; int len; { FSMDEBUG((stderr, "ppp: fsm_rconfack(%x): Rcvd id %d.\n", f->protocol, id)); switch (f->state) { case LISTEN: case CLOSED: fsm_sdata(f, TERMACK, id, NULL, 0); break; case ACKRCVD: case REQSENT: if (id != f->reqid) /* Expected id? */ break; /* Nope, toss... */ if (f->callbacks->ackci && (*f->callbacks->ackci)(f, inp, len)) /* Good ack? */ f->state = ACKRCVD; else f->state = REQSENT; /* Wait for timeout to retransmit */ break; case ACKSENT: if (id != f->reqid) /* Expected id? */ break; /* Nope, toss... */ if (f->callbacks->ackci && (*f->callbacks->ackci)(f, inp, len)) { /* Good ack? */ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->callbacks->up) (*f->callbacks->up)(f); /* Inform upper layers */ f->state = OPEN; } else f->state = REQSENT; /* Wait for timeout to retransmit */ break; case OPEN: if (f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ f->state = CLOSED; /* Only for a moment... */ fsm_activeopen(f); /* Restart */ break; } } /* * fsm_rconfnak - Receive Configure-Nak. */ void fsm_rconfnak(f, inp, id, len) fsm *f; u_char *inp; u_char id; int len; { FSMDEBUG((stderr, "ppp: fsm_rconfnak(%x): Rcvd id %d.\n", f->protocol, id)); switch (f->state) { case LISTEN: case CLOSED: fsm_sdata(f, TERMACK, id, NULL, 0); break; case REQSENT: case ACKSENT: if (id != f->reqid) /* Expected id? */ break; /* Nope, toss... */ if (++f->nakloops > f->maxnakloops) { FSMDEBUG((stderr, "ppp: fsm_rconfnak(%x): Possible CONFNAK loop!\n", f->protocol)); break; /* Break the loop */ } UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->callbacks->nakci) (*f->callbacks->nakci)(f, inp, len); fsm_sconfreq(f); /* Send Configure-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); ++f->retransmits; break; case ACKRCVD: f->state = REQSENT; /* Wait for timeout to retransmit */ break; case OPEN: if (f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ f->state = CLOSED; /* Only for a moment... */ fsm_activeopen(f); /* Restart */ break; } } /* * fsm_rconfrej - Receive Configure-Rej. */ void fsm_rconfrej(f, inp, id, len) fsm *f; u_char *inp; u_char id; int len; { FSMDEBUG((stderr, "ppp: fsm_rconfrej(%x): Rcvd id %d.\n", f->protocol, id)); switch (f->state) { case LISTEN: case CLOSED: fsm_sdata(f, TERMACK, id, NULL, 0); break; case REQSENT: case ACKSENT: if (id != f->reqid) /* Expected id? */ break; /* Nope, toss... */ if (++f->nakloops > f->maxnakloops) break; /* Break the loop */ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ if (f->callbacks->rejci) (*f->callbacks->rejci)(f, inp, len); fsm_sconfreq(f); /* Send Configure-Request */ TIMEOUT(fsm_timeout, f, f->timeouttime); ++f->retransmits; break; case ACKRCVD: f->state = REQSENT; /* Wait for timeout to retransmit */ break; case OPEN: f->state = CLOSED; /* Only for a moment... */ fsm_activeopen(f); /* Restart */ break; } } /* * fsm_rtermreq - Receive Terminate-Req. */ void fsm_rtermreq(f, id) fsm *f; u_char id; { FSMDEBUG((stderr, "ppp: fsm_rtermreq(%x): Rcvd id %d.\n", f->protocol, id)); fsm_sdata(f, TERMACK, id, NULL, 0); switch (f->state) { case ACKRCVD: case ACKSENT: f->state = REQSENT; /* Start over but keep trying */ break; case OPEN: if (f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ break; } } /* * fsm_rtermack - Receive Terminate-Ack. */ void fsm_rtermack(f) fsm *f; { FSMDEBUG((stderr, "ppp: fsm_rtermack(%x).\n", f->protocol)); switch (f->state) { case OPEN: if (f->callbacks->down) (*f->callbacks->down)(f); /* Inform upper layers */ f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ break; case TERMSENT: UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ f->state = CLOSED; if (f->callbacks->closed) (*f->callbacks->closed)(f); /* Exit/restart/etc. */ break; } } /* * fsm_rcoderej - Receive an Code-Reject. */ void fsm_rcoderej(f, inp, len) fsm *f; u_char *inp; int len; { u_char code; FSMDEBUG((stderr, "ppp: fsm_rcoderej(%x).\n", f->protocol)); if (len < sizeof (u_char)) { FSMDEBUG((stderr, "ppp: fsm_rcoderej: Rcvd short Code-Reject packet!\n")); return; } GETCHAR(code, inp); FSMDEBUG((stderr, "ppp: fsm_rcoderej: Rcvd Code-Reject for code %d!\n", code)); } /* * fsm_rprotrej - Receive an Protocol-Reject. * * Figure out which protocol is rejected and inform it. */ void fsm_rprotrej(f, inp, len) fsm *f; u_char *inp; int len; { u_short prot; FSMDEBUG((stderr, "ppp: fsm_rprotrej.\n")); if (len < sizeof (u_short)) { FSMDEBUG((stderr, "ppp: fsm_rprotrej: Rcvd short Protocol-Reject packet!\n")); return; } if (f->protocol != LCP) { /* Only valid for LCP */ FSMDEBUG((stderr, "ppp: fsm_rprotrej: Rcvd non-LCP Protocol-Reject!\n")); return; } GETSHORT(prot, inp); FSMDEBUG((stderr, "ppp: fsm_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); DEMUXPROTREJ(f->unit, prot); /* Inform protocol */ } /* * fsm_sconfreq - Send a Configure-Request. */ void fsm_sconfreq(f) fsm *f; { PACKET *outpacket; u_char *outp; int outlen; outlen = HEADERLEN + (f->callbacks->cilen ? (*f->callbacks->cilen)(f) : 0); /* XXX Adjust outlen to MTU */ outpacket = PACKET_ALLOC(outlen); outp = PACKET_DATA(outpacket); PUTCHAR(CONFREQ, outp); PUTCHAR(f->reqid = ++f->id, outp); PUTSHORT(outlen, outp); if (f->callbacks->cilen && f->callbacks->addci) (*f->callbacks->addci)(f, outp); OUTPUT(f->unit, outpacket, outlen, f->protocol); FSMDEBUG((stderr, "ppp: fsm_sconfreq(%x): Sent id %d.\n", f->protocol, f->reqid)); } /* * fsm_sdata - Send some data. * * Used for Terminate-Request, Terminate-Ack, Code-Reject, Protocol-Reject, * Echo-Request, and Discard-Request. */ void fsm_sdata(f, code, id, data, datalen) fsm *f; u_char code, id; u_char *data; int datalen; { PACKET *outpacket; u_char *outp; int outlen; /* Adjust length to be smaller than MTU */ if (datalen > MTU - HEADERLEN) datalen = MTU - HEADERLEN; outlen = datalen + HEADERLEN; outpacket = PACKET_ALLOC(outlen); outp = PACKET_DATA(outpacket); PUTCHAR(code, outp); PUTCHAR(id, outp); PUTSHORT(outlen, outp); if (datalen) BCOPY(data, outp, datalen); OUTPUT(f->unit, outpacket, outlen, f->protocol); FSMDEBUG((stderr, "ppp: fsm_sdata(%x): Sent code %d, id %d.\n", f->protocol, code, id)); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.