| /* |
| * fsm.c - {Link, IP} Control Protocol Finite State Machine. |
| * |
| * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The name "Carnegie Mellon University" must not be used to |
| * endorse or promote products derived from this software without |
| * prior written permission. For permission or any legal |
| * details, please contact |
| * Office of Technology Transfer |
| * Carnegie Mellon University |
| * 5000 Forbes Avenue |
| * Pittsburgh, PA 15213-3890 |
| * (412) 268-4387, fax: (412) 268-7395 |
| * tech-transfer@andrew.cmu.edu |
| * |
| * 4. Redistributions of any form whatsoever must retain the following |
| * acknowledgment: |
| * "This product includes software developed by Computing Services |
| * at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
| * |
| * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
| * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
| * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
| * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
| * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #include "netif/ppp/ppp_opts.h" |
| #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ |
| |
| /* |
| * @todo: |
| * Randomize fsm id on link/init. |
| * Deal with variable outgoing MTU. |
| */ |
| |
| #if 0 /* UNUSED */ |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #endif /* UNUSED */ |
| |
| #include "netif/ppp/ppp_impl.h" |
| |
| #include "netif/ppp/fsm.h" |
| |
| static void fsm_timeout (void *); |
| static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len); |
| static void fsm_rconfack(fsm *f, int id, u_char *inp, int len); |
| static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len); |
| static void fsm_rtermreq(fsm *f, int id, u_char *p, int len); |
| static void fsm_rtermack(fsm *f); |
| static void fsm_rcoderej(fsm *f, u_char *inp, int len); |
| static void fsm_sconfreq(fsm *f, int retransmit); |
| |
| #define PROTO_NAME(f) ((f)->callbacks->proto_name) |
| |
| /* |
| * fsm_init - Initialize fsm. |
| * |
| * Initialize fsm state. |
| */ |
| void fsm_init(fsm *f) { |
| ppp_pcb *pcb = f->pcb; |
| f->state = PPP_FSM_INITIAL; |
| f->flags = 0; |
| f->id = 0; /* XXX Start with random id? */ |
| f->maxnakloops = pcb->settings.fsm_max_nak_loops; |
| f->term_reason_len = 0; |
| } |
| |
| |
| /* |
| * fsm_lowerup - The lower layer is up. |
| */ |
| void fsm_lowerup(fsm *f) { |
| switch( f->state ){ |
| case PPP_FSM_INITIAL: |
| f->state = PPP_FSM_CLOSED; |
| break; |
| |
| case PPP_FSM_STARTING: |
| if( f->flags & OPT_SILENT ) |
| f->state = PPP_FSM_STOPPED; |
| else { |
| /* Send an initial configure-request */ |
| fsm_sconfreq(f, 0); |
| f->state = PPP_FSM_REQSENT; |
| } |
| break; |
| |
| default: |
| FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state)); |
| /* no break */ |
| } |
| } |
| |
| |
| /* |
| * fsm_lowerdown - The lower layer is down. |
| * |
| * Cancel all timeouts and inform upper layers. |
| */ |
| void fsm_lowerdown(fsm *f) { |
| switch( f->state ){ |
| case PPP_FSM_CLOSED: |
| f->state = PPP_FSM_INITIAL; |
| break; |
| |
| case PPP_FSM_STOPPED: |
| f->state = PPP_FSM_STARTING; |
| if( f->callbacks->starting ) |
| (*f->callbacks->starting)(f); |
| break; |
| |
| case PPP_FSM_CLOSING: |
| f->state = PPP_FSM_INITIAL; |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| break; |
| |
| case PPP_FSM_STOPPING: |
| case PPP_FSM_REQSENT: |
| case PPP_FSM_ACKRCVD: |
| case PPP_FSM_ACKSENT: |
| f->state = PPP_FSM_STARTING; |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| break; |
| |
| case PPP_FSM_OPENED: |
| if( f->callbacks->down ) |
| (*f->callbacks->down)(f); |
| f->state = PPP_FSM_STARTING; |
| break; |
| |
| default: |
| FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state)); |
| /* no break */ |
| } |
| } |
| |
| |
| /* |
| * fsm_open - Link is allowed to come up. |
| */ |
| void fsm_open(fsm *f) { |
| switch( f->state ){ |
| case PPP_FSM_INITIAL: |
| f->state = PPP_FSM_STARTING; |
| if( f->callbacks->starting ) |
| (*f->callbacks->starting)(f); |
| break; |
| |
| case PPP_FSM_CLOSED: |
| if( f->flags & OPT_SILENT ) |
| f->state = PPP_FSM_STOPPED; |
| else { |
| /* Send an initial configure-request */ |
| fsm_sconfreq(f, 0); |
| f->state = PPP_FSM_REQSENT; |
| } |
| break; |
| |
| case PPP_FSM_CLOSING: |
| f->state = PPP_FSM_STOPPING; |
| /* fall through */ |
| /* no break */ |
| case PPP_FSM_STOPPED: |
| case PPP_FSM_OPENED: |
| if( f->flags & OPT_RESTART ){ |
| fsm_lowerdown(f); |
| fsm_lowerup(f); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* |
| * terminate_layer - Start process of shutting down the FSM |
| * |
| * Cancel any timeout running, notify upper layers we're done, and |
| * send a terminate-request message as configured. |
| */ |
| static void terminate_layer(fsm *f, int nextstate) { |
| ppp_pcb *pcb = f->pcb; |
| |
| if( f->state != PPP_FSM_OPENED ) |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| else if( f->callbacks->down ) |
| (*f->callbacks->down)(f); /* Inform upper layers we're down */ |
| |
| /* Init restart counter and send Terminate-Request */ |
| f->retransmits = pcb->settings.fsm_max_term_transmits; |
| fsm_sdata(f, TERMREQ, f->reqid = ++f->id, |
| (const u_char *) f->term_reason, f->term_reason_len); |
| |
| if (f->retransmits == 0) { |
| /* |
| * User asked for no terminate requests at all; just close it. |
| * We've already fired off one Terminate-Request just to be nice |
| * to the peer, but we're not going to wait for a reply. |
| */ |
| f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| return; |
| } |
| |
| TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); |
| --f->retransmits; |
| |
| f->state = nextstate; |
| } |
| |
| /* |
| * fsm_close - Start closing connection. |
| * |
| * Cancel timeouts and either initiate close or possibly go directly to |
| * the PPP_FSM_CLOSED state. |
| */ |
| void fsm_close(fsm *f, const char *reason) { |
| f->term_reason = reason; |
| f->term_reason_len = (reason == NULL? 0: LWIP_MIN(strlen(reason), 0xFF) ); |
| switch( f->state ){ |
| case PPP_FSM_STARTING: |
| f->state = PPP_FSM_INITIAL; |
| break; |
| case PPP_FSM_STOPPED: |
| f->state = PPP_FSM_CLOSED; |
| break; |
| case PPP_FSM_STOPPING: |
| f->state = PPP_FSM_CLOSING; |
| break; |
| |
| case PPP_FSM_REQSENT: |
| case PPP_FSM_ACKRCVD: |
| case PPP_FSM_ACKSENT: |
| case PPP_FSM_OPENED: |
| terminate_layer(f, PPP_FSM_CLOSING); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| /* |
| * fsm_timeout - Timeout expired. |
| */ |
| static void fsm_timeout(void *arg) { |
| fsm *f = (fsm *) arg; |
| ppp_pcb *pcb = f->pcb; |
| |
| switch (f->state) { |
| case PPP_FSM_CLOSING: |
| case PPP_FSM_STOPPING: |
| if( f->retransmits <= 0 ){ |
| /* |
| * We've waited for an ack long enough. Peer probably heard us. |
| */ |
| f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| } else { |
| /* Send Terminate-Request */ |
| fsm_sdata(f, TERMREQ, f->reqid = ++f->id, |
| (const u_char *) f->term_reason, f->term_reason_len); |
| TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); |
| --f->retransmits; |
| } |
| break; |
| |
| case PPP_FSM_REQSENT: |
| case PPP_FSM_ACKRCVD: |
| case PPP_FSM_ACKSENT: |
| if (f->retransmits <= 0) { |
| ppp_warn("%s: timeout sending Config-Requests", PROTO_NAME(f)); |
| f->state = PPP_FSM_STOPPED; |
| if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| |
| } else { |
| /* Retransmit the configure-request */ |
| if (f->callbacks->retransmit) |
| (*f->callbacks->retransmit)(f); |
| fsm_sconfreq(f, 1); /* Re-send Configure-Request */ |
| if( f->state == PPP_FSM_ACKRCVD ) |
| f->state = PPP_FSM_REQSENT; |
| } |
| break; |
| |
| default: |
| FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state)); |
| /* no break */ |
| } |
| } |
| |
| |
| /* |
| * fsm_input - Input packet. |
| */ |
| void fsm_input(fsm *f, u_char *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 = inpacket; |
| if (l < HEADERLEN) { |
| FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); |
| return; |
| } |
| GETCHAR(code, inp); |
| GETCHAR(id, inp); |
| GETSHORT(len, inp); |
| if (len < HEADERLEN) { |
| FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); |
| return; |
| } |
| if (len > l) { |
| FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); |
| return; |
| } |
| len -= HEADERLEN; /* subtract header length */ |
| |
| if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){ |
| FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", |
| f->protocol, f->state)); |
| return; |
| } |
| |
| /* |
| * Action depends on code. |
| */ |
| switch (code) { |
| case CONFREQ: |
| fsm_rconfreq(f, id, inp, len); |
| break; |
| |
| case CONFACK: |
| fsm_rconfack(f, id, inp, len); |
| break; |
| |
| case CONFNAK: |
| case CONFREJ: |
| fsm_rconfnakrej(f, code, id, inp, len); |
| break; |
| |
| case TERMREQ: |
| fsm_rtermreq(f, id, inp, len); |
| break; |
| |
| case TERMACK: |
| fsm_rtermack(f); |
| break; |
| |
| case CODEREJ: |
| fsm_rcoderej(f, inp, len); |
| break; |
| |
| default: |
| if( !f->callbacks->extcode |
| || !(*f->callbacks->extcode)(f, code, id, inp, len) ) |
| fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); |
| break; |
| } |
| } |
| |
| |
| /* |
| * fsm_rconfreq - Receive Configure-Request. |
| */ |
| static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) { |
| int code, reject_if_disagree; |
| |
| switch( f->state ){ |
| case PPP_FSM_CLOSED: |
| /* Go away, we're closed */ |
| fsm_sdata(f, TERMACK, id, NULL, 0); |
| return; |
| case PPP_FSM_CLOSING: |
| case PPP_FSM_STOPPING: |
| return; |
| |
| case PPP_FSM_OPENED: |
| /* Go down and restart negotiation */ |
| if( f->callbacks->down ) |
| (*f->callbacks->down)(f); /* Inform upper layers */ |
| fsm_sconfreq(f, 0); /* Send initial Configure-Request */ |
| f->state = PPP_FSM_REQSENT; |
| break; |
| |
| case PPP_FSM_STOPPED: |
| /* Negotiation started by our peer */ |
| fsm_sconfreq(f, 0); /* Send initial Configure-Request */ |
| f->state = PPP_FSM_REQSENT; |
| break; |
| default: |
| break; |
| } |
| |
| /* |
| * Pass the requested configuration options |
| * to protocol-specific code for checking. |
| */ |
| if (f->callbacks->reqci){ /* Check CI */ |
| reject_if_disagree = (f->nakloops >= f->maxnakloops); |
| code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); |
| } else if (len) |
| code = CONFREJ; /* Reject all CI */ |
| else |
| code = CONFACK; |
| |
| /* send the Ack, Nak or Rej to the peer */ |
| fsm_sdata(f, code, id, inp, len); |
| |
| if (code == CONFACK) { |
| if (f->state == PPP_FSM_ACKRCVD) { |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| f->state = PPP_FSM_OPENED; |
| if (f->callbacks->up) |
| (*f->callbacks->up)(f); /* Inform upper layers */ |
| } else |
| f->state = PPP_FSM_ACKSENT; |
| f->nakloops = 0; |
| |
| } else { |
| /* we sent CONFACK or CONFREJ */ |
| if (f->state != PPP_FSM_ACKRCVD) |
| f->state = PPP_FSM_REQSENT; |
| if( code == CONFNAK ) |
| ++f->nakloops; |
| } |
| } |
| |
| |
| /* |
| * fsm_rconfack - Receive Configure-Ack. |
| */ |
| static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) { |
| ppp_pcb *pcb = f->pcb; |
| |
| if (id != f->reqid || f->seen_ack) /* Expected id? */ |
| return; /* Nope, toss... */ |
| if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): |
| (len == 0)) ){ |
| /* Ack is bad - ignore it */ |
| ppp_error("Received bad configure-ack: %P", inp, len); |
| return; |
| } |
| f->seen_ack = 1; |
| f->rnakloops = 0; |
| |
| switch (f->state) { |
| case PPP_FSM_CLOSED: |
| case PPP_FSM_STOPPED: |
| fsm_sdata(f, TERMACK, id, NULL, 0); |
| break; |
| |
| case PPP_FSM_REQSENT: |
| f->state = PPP_FSM_ACKRCVD; |
| f->retransmits = pcb->settings.fsm_max_conf_req_transmits; |
| break; |
| |
| case PPP_FSM_ACKRCVD: |
| /* Huh? an extra valid Ack? oh well... */ |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| fsm_sconfreq(f, 0); |
| f->state = PPP_FSM_REQSENT; |
| break; |
| |
| case PPP_FSM_ACKSENT: |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| f->state = PPP_FSM_OPENED; |
| f->retransmits = pcb->settings.fsm_max_conf_req_transmits; |
| if (f->callbacks->up) |
| (*f->callbacks->up)(f); /* Inform upper layers */ |
| break; |
| |
| case PPP_FSM_OPENED: |
| /* Go down and restart negotiation */ |
| if (f->callbacks->down) |
| (*f->callbacks->down)(f); /* Inform upper layers */ |
| fsm_sconfreq(f, 0); /* Send initial Configure-Request */ |
| f->state = PPP_FSM_REQSENT; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| /* |
| * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. |
| */ |
| static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) { |
| int ret; |
| int treat_as_reject; |
| |
| if (id != f->reqid || f->seen_ack) /* Expected id? */ |
| return; /* Nope, toss... */ |
| |
| if (code == CONFNAK) { |
| ++f->rnakloops; |
| treat_as_reject = (f->rnakloops >= f->maxnakloops); |
| if (f->callbacks->nakci == NULL |
| || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) { |
| ppp_error("Received bad configure-nak: %P", inp, len); |
| return; |
| } |
| } else { |
| f->rnakloops = 0; |
| if (f->callbacks->rejci == NULL |
| || !(ret = f->callbacks->rejci(f, inp, len))) { |
| ppp_error("Received bad configure-rej: %P", inp, len); |
| return; |
| } |
| } |
| |
| f->seen_ack = 1; |
| |
| switch (f->state) { |
| case PPP_FSM_CLOSED: |
| case PPP_FSM_STOPPED: |
| fsm_sdata(f, TERMACK, id, NULL, 0); |
| break; |
| |
| case PPP_FSM_REQSENT: |
| case PPP_FSM_ACKSENT: |
| /* They didn't agree to what we wanted - try another request */ |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| if (ret < 0) |
| f->state = PPP_FSM_STOPPED; /* kludge for stopping CCP */ |
| else |
| fsm_sconfreq(f, 0); /* Send Configure-Request */ |
| break; |
| |
| case PPP_FSM_ACKRCVD: |
| /* Got a Nak/reject when we had already had an Ack?? oh well... */ |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| fsm_sconfreq(f, 0); |
| f->state = PPP_FSM_REQSENT; |
| break; |
| |
| case PPP_FSM_OPENED: |
| /* Go down and restart negotiation */ |
| if (f->callbacks->down) |
| (*f->callbacks->down)(f); /* Inform upper layers */ |
| fsm_sconfreq(f, 0); /* Send initial Configure-Request */ |
| f->state = PPP_FSM_REQSENT; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| /* |
| * fsm_rtermreq - Receive Terminate-Req. |
| */ |
| static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) { |
| ppp_pcb *pcb = f->pcb; |
| |
| switch (f->state) { |
| case PPP_FSM_ACKRCVD: |
| case PPP_FSM_ACKSENT: |
| f->state = PPP_FSM_REQSENT; /* Start over but keep trying */ |
| break; |
| |
| case PPP_FSM_OPENED: |
| if (len > 0) { |
| ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); |
| } else |
| ppp_info("%s terminated by peer", PROTO_NAME(f)); |
| f->retransmits = 0; |
| f->state = PPP_FSM_STOPPING; |
| if (f->callbacks->down) |
| (*f->callbacks->down)(f); /* Inform upper layers */ |
| TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); |
| break; |
| default: |
| break; |
| } |
| |
| fsm_sdata(f, TERMACK, id, NULL, 0); |
| } |
| |
| |
| /* |
| * fsm_rtermack - Receive Terminate-Ack. |
| */ |
| static void fsm_rtermack(fsm *f) { |
| switch (f->state) { |
| case PPP_FSM_CLOSING: |
| UNTIMEOUT(fsm_timeout, f); |
| f->state = PPP_FSM_CLOSED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| break; |
| case PPP_FSM_STOPPING: |
| UNTIMEOUT(fsm_timeout, f); |
| f->state = PPP_FSM_STOPPED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| break; |
| |
| case PPP_FSM_ACKRCVD: |
| f->state = PPP_FSM_REQSENT; |
| break; |
| |
| case PPP_FSM_OPENED: |
| if (f->callbacks->down) |
| (*f->callbacks->down)(f); /* Inform upper layers */ |
| fsm_sconfreq(f, 0); |
| f->state = PPP_FSM_REQSENT; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| /* |
| * fsm_rcoderej - Receive an Code-Reject. |
| */ |
| static void fsm_rcoderej(fsm *f, u_char *inp, int len) { |
| u_char code, id; |
| |
| if (len < HEADERLEN) { |
| FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); |
| return; |
| } |
| GETCHAR(code, inp); |
| GETCHAR(id, inp); |
| ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); |
| |
| if( f->state == PPP_FSM_ACKRCVD ) |
| f->state = PPP_FSM_REQSENT; |
| } |
| |
| |
| /* |
| * fsm_protreject - Peer doesn't speak this protocol. |
| * |
| * Treat this as a catastrophic error (RXJ-). |
| */ |
| void fsm_protreject(fsm *f) { |
| switch( f->state ){ |
| case PPP_FSM_CLOSING: |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| /* fall through */ |
| /* no break */ |
| case PPP_FSM_CLOSED: |
| f->state = PPP_FSM_CLOSED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| break; |
| |
| case PPP_FSM_STOPPING: |
| case PPP_FSM_REQSENT: |
| case PPP_FSM_ACKRCVD: |
| case PPP_FSM_ACKSENT: |
| UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ |
| /* fall through */ |
| /* no break */ |
| case PPP_FSM_STOPPED: |
| f->state = PPP_FSM_STOPPED; |
| if( f->callbacks->finished ) |
| (*f->callbacks->finished)(f); |
| break; |
| |
| case PPP_FSM_OPENED: |
| terminate_layer(f, PPP_FSM_STOPPING); |
| break; |
| |
| default: |
| FSMDEBUG(("%s: Protocol-reject event in state %d!", |
| PROTO_NAME(f), f->state)); |
| /* no break */ |
| } |
| } |
| |
| |
| /* |
| * fsm_sconfreq - Send a Configure-Request. |
| */ |
| static void fsm_sconfreq(fsm *f, int retransmit) { |
| ppp_pcb *pcb = f->pcb; |
| struct pbuf *p; |
| u_char *outp; |
| int cilen; |
| |
| if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){ |
| /* Not currently negotiating - reset options */ |
| if( f->callbacks->resetci ) |
| (*f->callbacks->resetci)(f); |
| f->nakloops = 0; |
| f->rnakloops = 0; |
| } |
| |
| if( !retransmit ){ |
| /* New request - reset retransmission counter, use new ID */ |
| f->retransmits = pcb->settings.fsm_max_conf_req_transmits; |
| f->reqid = ++f->id; |
| } |
| |
| f->seen_ack = 0; |
| |
| /* |
| * Make up the request packet |
| */ |
| if( f->callbacks->cilen && f->callbacks->addci ){ |
| cilen = (*f->callbacks->cilen)(f); |
| if( cilen > pcb->peer_mru - HEADERLEN ) |
| cilen = pcb->peer_mru - HEADERLEN; |
| } else |
| cilen = 0; |
| |
| p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); |
| if(NULL == p) |
| return; |
| if(p->tot_len != p->len) { |
| pbuf_free(p); |
| return; |
| } |
| |
| /* send the request to our peer */ |
| outp = (u_char*)p->payload; |
| MAKEHEADER(outp, f->protocol); |
| PUTCHAR(CONFREQ, outp); |
| PUTCHAR(f->reqid, outp); |
| PUTSHORT(cilen + HEADERLEN, outp); |
| if (cilen != 0) { |
| (*f->callbacks->addci)(f, outp, &cilen); |
| LWIP_ASSERT("cilen == p->len - HEADERLEN - PPP_HDRLEN", cilen == p->len - HEADERLEN - PPP_HDRLEN); |
| } |
| |
| ppp_write(pcb, p); |
| |
| /* start the retransmit timer */ |
| --f->retransmits; |
| TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); |
| } |
| |
| |
| /* |
| * fsm_sdata - Send some data. |
| * |
| * Used for all packets sent to our peer by this module. |
| */ |
| void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen) { |
| ppp_pcb *pcb = f->pcb; |
| struct pbuf *p; |
| u_char *outp; |
| int outlen; |
| |
| /* Adjust length to be smaller than MTU */ |
| if (datalen > pcb->peer_mru - HEADERLEN) |
| datalen = pcb->peer_mru - HEADERLEN; |
| outlen = datalen + HEADERLEN; |
| |
| p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); |
| if(NULL == p) |
| return; |
| if(p->tot_len != p->len) { |
| pbuf_free(p); |
| return; |
| } |
| |
| outp = (u_char*)p->payload; |
| if (datalen) /* && data != outp + PPP_HDRLEN + HEADERLEN) -- was only for fsm_sconfreq() */ |
| MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen); |
| MAKEHEADER(outp, f->protocol); |
| PUTCHAR(code, outp); |
| PUTCHAR(id, outp); |
| PUTSHORT(outlen, outp); |
| ppp_write(pcb, p); |
| } |
| |
| #endif /* PPP_SUPPORT */ |