www.pudn.com > 蓝牙协议源代码.zip > ppp.c
/* * Copyright (c) 2003 EISLAB, Lulea University of Technology. * 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 of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwBT Bluetooth stack. * * Author: Conny Ohult* */ /*-----------------------------------------------------------------------------------*/ /* ppp.c * * Implementation of the PPP protocol. * Used to carry packets from the higher IP layer across the RFCOMM serial port * emulation layer. */ /*-----------------------------------------------------------------------------------*/ #include "netif/lwbt/rfcomm.h" #include "netif/lwbt/ppp.h" #include "netif/lwbt/lwbt_memp.h" #include "netif/lwbt/fcs.h" #include "lwbtopts.h" #include "lwip/debug.h" struct ppp_pcb *ppp_listen_pcbs; /* List of all PPP PCBs listening for a connection */ struct ppp_pcb *ppp_active_pcbs; /* List of all active PPP PCBs */ struct ppp_pcb *ppp_tmp_pcb; struct ppp_req *ppp_tmp_req; u8_t id_nxt; /* Next Identifier to be sent */ /* Forward declarations */ err_t ppp_output(struct ppp_pcb *pcb, struct pbuf *p); err_t ppp_cp_output(struct ppp_pcb *pcb, u16_t proto, u8_t code, u8_t id, struct pbuf *q); u8_t ppp_next_id(void); struct pbuf *lcp_cfg_req(struct ppp_pcb *pcb, struct pbuf *options); err_t ipcp_cfg_req(struct ppp_pcb *pcb); void ppp_pbuf_ref_chain(struct pbuf *p); /*-----------------------------------------------------------------------------------*/ /* * ppp_init(): * * Initializes the PPP layer. */ /*-----------------------------------------------------------------------------------*/ void ppp_init(void) { /* Clear globals */ ppp_active_pcbs = NULL; ppp_tmp_pcb = NULL; /* Initialize the lcp and ipcp identifier */ id_nxt = 0; } /*-----------------------------------------------------------------------------------*/ /* * ppp_tmr(): * * Called every 1s and implements the command timer that * removes a DLC if it has been waiting for a response enough * time. */ /*-----------------------------------------------------------------------------------*/ void ppp_tmr(void) { struct ppp_pcb *pcb; struct ppp_req *req; err_t ret; /* Step through all of the active pcbs */ for(pcb = ppp_active_pcbs; pcb != NULL; pcb = pcb->next) { /* Step through any unresponded requests */ for(req = pcb->reqs; req != NULL; req = req->next) { --req->rto; /* Adjust rto timer */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_tmr: req->rto = %d\n", req->rto)); /* Check if restart timer has expired */ if(req->rto == 0) { /* Check if max number of retransmissions have been reached */ if(req->nrtx == 0) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_tmr: Max number of retransmissions have been reached\n")); PPP_REQ_RMV(&(pcb->reqs), req); pbuf_free(req->p); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_tmr: Free memory for request with ID 0x%x***********\n", req->id)); lwbt_memp_free(MEMP_PPP_REQ, req); pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb,ERR_CLSD,req->proto,ret); } else { --req->nrtx; /* Retransmitt request with timeout doubled. It may not exceed the configured timeout value */ req->rto = PPP_RTO/(req->nrtx + 1); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_tmr: Retransmitt\n")); ppp_output(pcb, req->p); } } } } } /*-----------------------------------------------------------------------------------*/ /* * ppp_lp_disconnected(): * * Called by the application to indicate that the lower protocol disconnected. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_lp_disconnected(struct rfcomm_pcb *rfcommpcb) { struct ppp_pcb *pcb; err_t ret = ERR_OK; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_lp_disconnected\n")); for(pcb = ppp_active_pcbs; pcb != NULL; pcb = pcb->next) { if(pcb->rfcommpcb == rfcommpcb) { pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_LCP, ret); return ret; /* Since there should be only one PPP connection */ } } for(pcb = ppp_listen_pcbs; pcb != NULL; pcb = pcb->next) { if(pcb->rfcommpcb == rfcommpcb) { pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_LCP, ret); return ret; /* Since there should be only one PPP connection */ } } return ret; } /*-----------------------------------------------------------------------------------*/ /* * ppp_process_lcp(): * * Parses the received LCP packet and handles it. */ /*-----------------------------------------------------------------------------------*/ void ppp_process_lcp(struct ppp_pcb *pcb, struct pbuf *p) { struct ppp_cp_hdr *cphdr; struct ppp_cfg_hdr *cfghdr; struct ppp_req *req; struct pbuf *r, *s; u8_t rspstate = LCP_CFG_ACK; u16_t len; err_t ret; cphdr = p->payload; pbuf_header(p, -PPP_CPHDR_LEN); cphdr->len = ntohs(cphdr->len); len = cphdr->len - PPP_CPHDR_LEN; if(cphdr->code == LCP_CFG_ACK || cphdr->code == LCP_CFG_NAK || cphdr->code == LCP_CFG_REJ || cphdr->code == LCP_CODE_REJ || cphdr->code == LCP_TERM_ACK) { for(req = pcb->reqs; req != NULL; req = req->next) { /* Remove any matching request */ if(cphdr->id == req->id) { PPP_REQ_RMV(&(pcb->reqs), req); pbuf_free(req->p); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: Free memory for request with id 0x%x***********\n", req->id)); lwbt_memp_free(MEMP_PPP_REQ, req); } } } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: cphdr->code = 0x%x\n", cphdr->code)); /* { struct pbuf *q; for(q = p; q != NULL; q = q->next) { u16_t i; for(i = 0; i < q->len; ++i) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: 0x%x\n", ((u8_t *)q->payload)[i])); } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: q->len == %d q->tot_len == %d\n", q->len, q->tot_len)); } } */ r = NULL; switch(cphdr->code) { case LCP_CFG_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CFG_REQ\n")); if(pcb->state == PPP_LCP_CLOSED) { /* A terminate-ack is sent to indicate that we are in a closed state */ ppp_cp_output(pcb, PPP_LCP, LCP_TERM_ACK, ppp_next_id(), NULL); break; } else if(pcb->state == PPP_LCP_LISTEN) { /* If pcb in LISTEN state we move it to the active list */ PPP_RMV(&(ppp_listen_pcbs), pcb); PPP_REG(&ppp_active_pcbs, pcb); } pcb->state = PPP_LCP_CFG; while(len > 0) { cfghdr = p->payload; switch(cfghdr->type) { case LCP_CFG_MRU: /* ACK - Parameter accepted */ pcb->mru = ntohs(((u16_t *)p->payload)[1]); if(rspstate == LCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == LCP_CFG_NAK or LCP_CFG_REJ do not add packet to outgoing pbuf */ case LCP_CFG_ACCM: /* ACK - Parameter accepted */ /* Since we will handle all characters that are escaped we do not need to save the ACCM for incoming data */ if(rspstate == LCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == LCP_CFG_NAK or LCP_CFG_REJ do not add packet to outgoing pbuf */ break; #if PPP_AUTH case LCP_CFG_AUTH: //TODO: NOT IMPLEMENTDED break; #endif #if PPP_PHDR_COMP case LCP_CFG_P_COMP: pcb->lcpcfg |= LCP_CFG_IN_PCOMP; if(rspstate == LCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } break; #endif /* PPP_PHDR_COMP */ #if PPP_ACHDR_COMP case LCP_CFG_AC_COMP: pcb->lcpcfg |= LCP_CFG_IN_ACCOMP; if(rspstate == LCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } break; #endif /* PPP_ACHDR_COMP */ default: /* Reject parameter */ if(rspstate != LCP_CFG_REJ) { rspstate = LCP_CFG_REJ; if(r != NULL) { pbuf_free(r); r = NULL; } } s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } break; } /* switch */ //pbuf_header(p, -(cfghdr->len - PPP_CFGHDR_LEN)); //len -= cfghdr->len - PPP_CFGHDR_LEN; pbuf_header(p, -(cfghdr->len)); len -= cfghdr->len; } /* while */ if(!(pcb->pppcfg & PPP_IR) && !(pcb->lcpcfg2 & LCP_CFG_OUT_REQ)) { /* Send a LCP configure request for outgoing link if it hasnt been configured */ p = lcp_cfg_req(pcb, NULL); ppp_cp_output(pcb, PPP_LCP, LCP_CFG_REQ, ppp_next_id(), p); pcb->lcpcfg2 |= LCP_CFG_OUT_REQ; } /* Send response to configuration request */ ppp_cp_output(pcb, PPP_LCP, rspstate, cphdr->id, r); if(rspstate == LCP_CFG_ACK) { pcb->lcpcfg |= LCP_CFG_OUT_ACK; /* LCP connection established if a configuration a ack has been received */ if(pcb->lcpcfg & LCP_CFG_IN_ACK) { if(pcb->lcpcfg & LCP_CFG_PAP && pcb->pppcfg & PPP_IR) { //TODO: CALL AUTHENTICATE FUNCTION IN UPPER LAYER /* Authenticate if we are the initiator */ } else { pcb->state = PPP_IPCP_CFG; if(pcb->pppcfg & PPP_IR) { /* Configure IPCP connection if we are the initiator */ if(ipcp_cfg_req(pcb) != ERR_OK) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: ipcp config request failed\n")); ppp_disconnect(pcb); } } } } } break; case LCP_CFG_ACK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CFG_ACK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { /* A terminate-ack is sent to indicate that we are in a closed state */ ppp_cp_output(pcb, PPP_LCP, LCP_TERM_ACK, ppp_next_id(), NULL); break; } pcb->lcpcfg |= LCP_CFG_IN_ACK; pcb->naks = 0; while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { case LCP_CFG_MRU: /* Maximum receive unit that the implementation can receive. Doesnt need to be stored */ break; case LCP_CFG_ACCM: pcb->outaccm = ntohl(*((u32_t *)(((u16_t *)p->payload) + 1))); break; #if PPP_PHDR_COMP case LCP_CFG_P_COMP: pcb->lcpcfg |= LCP_CFG_OUT_PCOMP; break; #endif /* PPP_PHDR_COMP */ #if PPP_ACHDR_COMP case LCP_CFG_AC_COMP: pcb->lcpcfg |= LCP_CFG_OUT_ACCOMP; break; #endif /* PPP_ACHDR_COMP */ default: /* Silently discard configuration option */ break; } /* switch */ pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ /* LCP connection established if a configuration a ack has been sent */ if(pcb->lcpcfg & LCP_CFG_OUT_ACK) { /* LCP connection established */ pcb->state = PPP_IPCP_CFG; if(pcb->pppcfg & PPP_IR) { /* Configure IPCP connection if we are the initiator */ if(ipcp_cfg_req(pcb) != ERR_OK) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: ipcp config request failed\n")); ppp_disconnect(pcb); } } } break; case LCP_CFG_NAK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CFG_NAK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { /* A terminate-ack is sent to indicate that we are in a closed state */ ppp_cp_output(pcb, PPP_LCP, LCP_TERM_ACK, ppp_next_id(), NULL); break; } ++pcb->naks; if(pcb->naks == PPP_MAX_FAILURE) { pcb->state = PPP_LCP_CLOSED; PPP_EVENT_CONNECTED(pcb, ERR_CONN, ret); break; } while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { case LCP_CFG_MRU: /* Maximum receive unit that the implementation can receive is not accepted */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, MRU = %d\n", ntohs(((u16_t *)p->payload)[1]))); if(PPP_IN_MRU > ntohs(((u16_t *)p->payload)[1])) { pcb->mru = ntohs(((u16_t *)p->payload)[1]); } else { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: MRU negotiation failed\n")); ppp_disconnect(pcb); /* MRU negotiation failed */ return; } break; case LCP_CFG_ACCM: pcb->outaccm = ntohl(*((u32_t *)(((u16_t *)p->payload) + 1))); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, ACCM\n")); break; case LCP_CFG_AUTH: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, AUTH\n")); /* Check if authentication option was in request */ if(pcb->lcpopt & LCP_CFG_OPT_PAP) { //TODO: Remote PPP uses an authentication protocol that we do not understand. What to do??? } else { /* Configuration option is requested to be in next request */ pcb->lcpopt |= LCP_CFG_OPT_PAP; } break; #if PPP_PHDR_COMP case LCP_CFG_P_COMP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, P_COMP\n")); pcb->lcpopt &= ~LCP_CFG_OPT_PFC; break; #endif /* PPP_PHDR_COMP */ #if PPP_ACHDR_COMP case LCP_CFG_AC_COMP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, AC_COMP\n")); pcb->lcpopt &= ~LCP_CFG_OPT_ACFC; break; #endif /* PPP_ACHDR_COMP */ default: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: NAK, ?\n")); /* Silently discard configuration option */ break; } /* switch */ pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ s = lcp_cfg_req(pcb, r); ppp_cp_output(pcb, PPP_LCP, LCP_CFG_REQ, ppp_next_id(), s); break; case LCP_CFG_REJ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CFG_REJ\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { /* A terminate-ack is sent to indicate that we are in a closed state */ ppp_cp_output(pcb, PPP_LCP, LCP_TERM_ACK, ppp_next_id(), NULL); break; } while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { case LCP_CFG_MRU: pcb->lcpopt &= ~LCP_CFG_OPT_MRU; break; case LCP_CFG_ACCM: pcb->lcpopt &= ~LCP_CFG_OPT_ACCM; break; case LCP_CFG_AUTH: pcb->lcpopt &= ~LCP_CFG_OPT_PAP; break; #if PPP_PHDR_COMP case LCP_CFG_P_COMP: pcb->lcpopt &= ~LCP_CFG_OPT_PFC; break; #endif /* PPP_PHDR_COMP */ #if PPP_ACHDR_COMP case LCP_CFG_AC_COMP: pcb->lcpopt &= ~LCP_CFG_OPT_ACFC; break; #endif /* PPP_ACHDR_COMP */ default: break; } /* switch */ pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ s = lcp_cfg_req(pcb, NULL); ppp_cp_output(pcb, PPP_LCP, LCP_CFG_REQ, ppp_next_id(), s); break; case LCP_TERM_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_TERM_REQ\n")); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: Connection terminate request\n")); ppp_pbuf_ref_chain(p); /* Increase reference count so that ppp_cp_output don't free it */ ppp_cp_output(pcb, PPP_LCP, LCP_TERM_ACK, cphdr->id, p); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { /* We are already closed */ break; } pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_LCP, ret); break; case LCP_TERM_ACK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_TERM_ACK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { break; /* Term-acks are silently discarded in this state to avoid creating a loop */ } pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_LCP, ret); break; case LCP_CODE_REJ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CODE_REJ\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN) { break; /* Silently discarded */ } /* Handle a code reject depending on what code was rejected */ switch(*((u8_t *)p->payload)) { case LCP_CFG_REQ: case LCP_CFG_ACK: case LCP_CFG_NAK: case LCP_CFG_REJ: pcb->state = PPP_LCP_CLOSED; PPP_EVENT_CONNECTED(pcb, ERR_VAL, ret); break; case LCP_TERM_REQ: pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_VAL, PPP_LCP, ret); break; case LCP_ECHO_REQ: PPP_EVENT_ECHO_RSP(pcb, ERR_VAL, ret); break; default: /* Silently discard packet */ break; } case LCP_PROTO_REJ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_CODE_REJ\n")); /* All rejections are catastrophic to this implementation */ if(pcb->state == PPP_LCP_OPEN) { ppp_cp_output(pcb, PPP_LCP, LCP_TERM_REQ, ppp_next_id(), NULL); pcb->state = PPP_LCP_CLOSING; } else { pcb->state = PPP_LCP_CLOSED; PPP_EVENT_DISCONNECTED(pcb, ERR_VAL, *((u16_t *)p->payload), ret); } break; case LCP_ECHO_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_ECHO_REQ\n")); if(pcb->state < PPP_LCP_OPEN && pcb->state > PPP_IPCP_OPEN) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: Echo request silently discarded. State = %d\n", pcb->state)); break; /* Silently discarded */ } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: Send echo response\n")); pbuf_ref(p); ppp_cp_output(pcb, PPP_LCP, LCP_ECHO_RSP, cphdr->id, p); /* Send echo reply */ break; case LCP_ECHO_RSP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_ECHO_RSP\n")); PPP_EVENT_ECHO_RSP(pcb, ERR_OK, ret); break; case LCP_DISCARD_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_DISCARD_REQ\n")); /* Silently discard packet */ break; default: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: LCP_UNKNOWN\n")); /* Code reject */ pbuf_header(p, 4); /* The data field in the reject packet begin with the information field */ ppp_pbuf_ref_chain(p); /* Increase reference count so that ppp_cp_output don't free it */ ppp_cp_output(pcb, PPP_LCP, LCP_CODE_REJ, cphdr->id, p); break; } } /*-----------------------------------------------------------------------------------*/ /* * ppp_process_ipcp(): * * Parses the received IPCP packet and handles it. */ /*-----------------------------------------------------------------------------------*/ void ppp_process_ipcp(struct ppp_pcb *pcb, struct pbuf *p) { struct ppp_cp_hdr *cphdr; struct pbuf *r, *s; struct ppp_cfg_hdr *cfghdr; struct ppp_req *req; u8_t rspstate = IPCP_CFG_ACK; u8_t ipaddropt = 0; /* Used in IPCP config requests to check if we have received an ip address option. If not, we append it to a NAK since configuration about the remote IP-address is required */ err_t ret; //struct ppp_pcb *tpcb; u16_t len; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp\n")); /* Check if the remote IP-address should have been negotiated */ //for(tpcb = ppp_active_pcbs; tpcb != NULL; tpcb = tpcb->next) { // if(pcb->rfcommpcb == tpcb->rfcommpcb) { // break; /* We are serving a client that should request an IP-address from us */ // } //} cphdr = p->payload; pbuf_header(p, -PPP_CPHDR_LEN); cphdr->len = ntohs(cphdr->len); len = cphdr->len - PPP_CPHDR_LEN; if(cphdr->code == IPCP_CFG_ACK || cphdr->code == IPCP_CFG_NAK || cphdr->code == IPCP_CFG_REJ || cphdr->code == IPCP_CODE_REJ || cphdr->code == IPCP_TERM_ACK) { for(req = pcb->reqs; req != NULL; req = req->next) { /* Remove any matching request */ if(cphdr->id == req->id) { PPP_REQ_RMV(&(pcb->reqs), req); pbuf_free(req->p); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: Free memory for request with code 0x%x***********\n", req->id)); lwbt_memp_free(MEMP_PPP_REQ, req); } } } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: cphdr->code = 0x%x\n", cphdr->code)); /* { struct pbuf *q; for(q = p; q != NULL; q = q->next) { u16_t i; for(i = 0; i < q->len; ++i) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: 0x%x\n", ((u8_t *)q->payload)[i])); } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: q->len == %d q->tot_len == %d\n", q->len, q->tot_len)); } } */ r = NULL; switch(cphdr->code) { case IPCP_CFG_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CFG_REQ\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* A terminate-ack is sent to indicate that ipcp is in a closed state */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, ppp_next_id(), NULL); break; } pcb->state = PPP_IPCP_CFG; while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { #if PPP_VJ_COMP case IPCP_CFG_COMP: //TODO: IMPLEMENT pcb->ipcpcfg |= IPCP_CFG_IN_VJ; /* NAK - Parameter not acceptable */ /* if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = 0; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = 0; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } }*/ /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ break; #endif /* PPP_VJ_COMP */ case IPCP_CFG_IPADDR: ipaddropt = 1; //if(tpcb == NULL) { if(pcb->pppcfg & PPP_IR) { /* We are a client and the remote server has given us its ip-address */ pcb->bluetoothif->gw.addr = *((u32_t *)(((u16_t *)p->payload) + 1)); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("We are a client and the remote server has given us its ip-address\nppp_process_ipcp: Remote IP address: %d.%d.%d.%d %ld\n", (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 24) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 16) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 8) & 0xff, (u8_t)ntohl(pcb->bluetoothif->gw.addr) & 0xff, pcb->bluetoothif->gw.addr)); /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* We are serving a client that we provide with an ip-address */ if(*((u32_t *)(((u16_t *)p->payload) + 1)) == pcb->bluetoothif->gw.addr) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("We are serving a client that we have provided with an ip-address: %d.%d.%d.%d %ld\n", (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 24) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 16) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->gw.addr) >> 8) & 0xff, (u8_t)ntohl(pcb->bluetoothif->gw.addr) & 0xff, pcb->bluetoothif->gw.addr)); /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* NAK - Parameter not acceptable */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = pcb->bluetoothif->gw.addr; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)s->payload)+1)) = pcb->bluetoothif->gw.addr; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } } break; case IPCP_CFG_PDNS: if(*((u32_t *)(((u16_t *)p->payload) + 1)) == PPP_IPCP_PDNS) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CFG_PDNS ACK\n")); /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* NAK - Parameter not acceptable */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = PPP_IPCP_PDNS; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)s->payload)+1)) = PPP_IPCP_PDNS; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } break; case IPCP_CFG_PNBNS: if(*((u32_t *)(((u16_t *)p->payload) + 1)) == PPP_IPCP_NBNS) { /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* NAK - Parameter not acceptable */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = PPP_IPCP_NBNS; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)s->payload)+1)) = PPP_IPCP_NBNS; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } break; case IPCP_CFG_SDNS: if(*((u32_t *)(((u16_t *)p->payload) + 1)) == PPP_IPCP_SDNS) { /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* NAK - Parameter not acceptable */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = PPP_IPCP_SDNS; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)s->payload)+1)) = PPP_IPCP_SDNS; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } break; case IPCP_CFG_SNBNS: if(*((u32_t *)(((u16_t *)p->payload) + 1)) == PPP_IPCP_SNBNS) { /* ACK - Parameter accepted */ if(rspstate == IPCP_CFG_ACK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } } else { /* NAK - Parameter not acceptable */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); r = NULL; } r = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)r->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)r->payload)+1)) = PPP_IPCP_SNBNS; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, PPP_CFGHDR_LEN); *((u32_t *)(((u16_t *)s->payload)+1)) = PPP_IPCP_SNBNS; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } break; default: /* Reject parameter */ if(rspstate != IPCP_CFG_REJ) { rspstate = IPCP_CFG_REJ; if(r != NULL) { pbuf_free(r); r = NULL; } } s = pbuf_alloc(PBUF_RAW, cfghdr->len, PBUF_RAM); memcpy((u8_t *)s->payload, (u8_t *)p->payload, cfghdr->len); if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } break; } pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ /* Check if we are serving a client that should have requested an IP-address from us */ //if(!ipaddropt && tpcb != NULL) { if(!ipaddropt && !(pcb->pppcfg & PPP_IR)) { /* NAK - the remote IP-address must be negotiated */ if(rspstate == IPCP_CFG_ACK) { if(r != NULL) { pbuf_free(r); } r = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 4, PBUF_RAM); cfghdr = r->payload; cfghdr->type = IPCP_CFG_IPADDR; cfghdr->len = PPP_CFGHDR_LEN + 4; *((u32_t *)(((u16_t *)r->payload)+1)) = pcb->bluetoothif->gw.addr; rspstate = IPCP_CFG_NAK; } else if (rspstate == IPCP_CFG_NAK) { s = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 4, PBUF_RAM); cfghdr = s->payload; cfghdr->type = IPCP_CFG_IPADDR; cfghdr->len = PPP_CFGHDR_LEN + 4; *((u32_t *)(((u16_t *)s->payload)+1)) = pcb->bluetoothif->gw.addr; if(r == NULL) { r = s; } else { pbuf_chain(r, s); pbuf_free(s); } } /* if rspstate == IPCP_CFG_REJ do not add packet to outgoing pbuf */ } if(!(pcb->pppcfg & PPP_IR) && !(pcb->ipcpcfg & IPCP_CFG_OUT_REQ)) { /* Send an IPCP configure request for outgoing link if it hasnt been configured */ ipcp_cfg_req(pcb); pcb->ipcpcfg |= IPCP_CFG_OUT_REQ; } /* Send response to configuration request */ ppp_cp_output(pcb, PPP_IPCP, rspstate, cphdr->id, r); if(rspstate == IPCP_CFG_ACK) { pcb->ipcpcfg |= IPCP_CFG_OUT_ACK; /* IPCP connection established if a configuration ack has been sent */ if(pcb->ipcpcfg & IPCP_CFG_IN_ACK) { /* IPCP connection established, notify upper layer that connection is open */ pcb->state = PPP_IPCP_OPEN; PPP_EVENT_CONNECTED(pcb, ERR_OK, ret); } } break; case IPCP_CFG_ACK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CFG_ACK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* A terminate-ack is sent to indicate that ipcp is in a closed state */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, ppp_next_id(), NULL); break; } pcb->ipcpcfg |= IPCP_CFG_IN_ACK; pcb->naks = 0; while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { #if PPP_VJ_COMP case IPCP_CFG_COMP: //TODO: IMPLEMENT pcb->ipcpcfg |= IPCP_CFG_OUT_VJ; break; #endif /* PPP_VJ_COMP */ default: /* Silently discard option */ break; } /* switch */ pbuf_header(p, -(cfghdr->len)); len -= cfghdr->len; } /* while */ /* IPCP connection established if a configuration ack has been sent */ if(pcb->ipcpcfg & IPCP_CFG_OUT_ACK) { /* IPCP connection established, notify upper layer that connection is open */ pcb->state = PPP_IPCP_OPEN; PPP_EVENT_CONNECTED(pcb, ERR_OK, ret); } break; case IPCP_CFG_NAK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CFG_NAK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* A terminate-ack is sent to indicate that ipcp is in a closed state */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, ppp_next_id(), NULL); break; } ++pcb->naks; if(pcb->naks == PPP_MAX_FAILURE) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_lcp: ipcp negotiation failed\n")); ppp_disconnect(pcb); /* IPCP configuration attempt failed. Disconnect LCP */ PPP_EVENT_CONNECTED(pcb, ERR_CONN, ret); break; } while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { #if PPP_VJ_COMP case IPCP_CFG_COMP: //TODO: IMPLEMENT pcb->ipcpopt &= ~IPCP_CFG_OPT_VJ; break; #endif /* PPP_VJ_COMP */ case IPCP_CFG_IPADDR: /* We have been given a local ip address by the remote host */ pcb->bluetoothif->ip_addr.addr = *((u32_t *)(((u16_t *)p->payload) + 1)); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: Local IP address: %d.%d.%d.%d %ld\n", (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 24) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 16) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 8) & 0xff, (u8_t)ntohl(pcb->bluetoothif->ip_addr.addr) & 0xff, pcb->bluetoothif->ip_addr.addr)); break; default: /* Silently discard configuration option */ break; } /* switch */ pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ ipcp_cfg_req(pcb); /* Send new ipcp configuration request */ break; case IPCP_CFG_REJ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CFG_REJ\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* A terminate-ack is sent to indicate that ipcp is in a closed state */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, ppp_next_id(), NULL); break; } while(len > 0) { cfghdr = p->payload; //pbuf_header(p, -PPP_CFGHDR_LEN); //len -= PPP_CFGHDR_LEN; switch(cfghdr->type) { #if PPP_VJ_COMP case IPCP_CFG_COMP: pcb->ipcpopt &= ~IPCP_CFG_OPT_VJ; break; #endif /* PPP_VJ_COMP */ case IPCP_CFG_IPADDR: pcb->ipcpopt &= ~IPCP_CFG_OPT_IP; break; default: /* Silently discard configuration option */ break; } /* switch */ pbuf_header(p, -cfghdr->len); len -= cfghdr->len; } /* while */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: Send new ipcp configuration request\n")); ipcp_cfg_req(pcb); /* Send new ipcp configuration request */ break; case IPCP_TERM_REQ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_TERM_REQ\n")); ppp_pbuf_ref_chain(p); /* Increase reference count so that ppp_cp_output don't delete the buffer */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, cphdr->id, p); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* We are already closed */ break; } pcb->state = PPP_LCP_OPEN; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_IPCP, ret); break; case IPCP_TERM_ACK: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_TERM_ACK\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { break; /* Term-acks are silently discarded in this state to avoid creating a loop */ } pcb->state = PPP_LCP_OPEN; PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_IPCP, ret); break; case IPCP_CODE_REJ: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_CODE_REJ\n")); if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { /* A terminate-ack is sent to indicate that ipcp is in a closed state */ ppp_cp_output(pcb, PPP_IPCP, IPCP_TERM_ACK, ppp_next_id(), NULL); break; } /* Handle a code reject depending on what code was rejected */ switch(*((u8_t *)p->payload)) { case IPCP_CFG_REQ: case IPCP_CFG_ACK: case IPCP_CFG_NAK: case IPCP_CFG_REJ: pcb->state = PPP_LCP_OPEN; PPP_EVENT_CONNECTED(pcb, ERR_VAL, ret); break; case IPCP_TERM_REQ: pcb->state = PPP_LCP_OPEN; PPP_EVENT_DISCONNECTED(pcb, ERR_VAL, PPP_IPCP, ret); break; default: /* Silently discard */ break; } break; default: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_ipcp: IPCP_UNKNOWN\n")); /* Code reject */ pbuf_header(p, 4); /* The data field in the reject packet begin with the information field */ ppp_pbuf_ref_chain(p); /* Increase reference count so that ppp_cp_output don't free it */ ppp_cp_output(pcb, PPP_IPCP, IPCP_CODE_REJ, cphdr->id, p); break; } } /*-----------------------------------------------------------------------------------*/ /* * ppp_process_pap(): * * Parses the received PAP packet and handles it. */ /*-----------------------------------------------------------------------------------*/ void ppp_process_pap(struct ppp_pcb *pcb, struct pbuf *p) { //TODO: Implement LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_process_pap: not implemented yet!\n")); } /*-----------------------------------------------------------------------------------*/ /* * ppp_input(): * * Called by the lower layer. Reassembles and unstuffs the packet, does a frame check, * parses the header and forward it to the upper layer, LCP, IPCP or PAP handler. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_input(void *arg, struct rfcomm_pcb *rfcommpcb, struct pbuf *p, err_t err) { struct ppp_pcb *pcb; u16_t proto; struct pbuf *q, *r; u8_t *payload; err_t ret; u16_t i; /* Search active pcbs for matching connection */ for(pcb = ppp_active_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Found matching connection\n")); if(pcb->rfcommpcb == rfcommpcb) { break; } } if(pcb == NULL) { for(pcb = ppp_listen_pcbs; pcb != NULL; pcb = pcb->next) { if(pcb->rfcommpcb == rfcommpcb) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Found matching listening connection\n")); break; } } if(pcb == NULL) { /* Silently discard incoming data not belonging to any RFCOMM connection */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Silently discard incoming data not belonging to any RFCOMM connection\n")); pbuf_free(p); return ERR_OK; } } for(q = p; q != NULL; q = q->next) { payload = (u8_t *)q->payload; for(i = 0; i < q->len; ++i) { if(pcb->p == NULL) { /* Alloc new pbuf. LwIP will handle dealloc */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Pbuf == NULL. Allocate and add new head\n")); if((pcb->p = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL)) == NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Could not allocate memory for pbuf pcb->p\n")); pbuf_free(p); return ERR_MEM; /* Could not allocate memory for pbuf */ } pcb->q = pcb->p; /* Make q the pointer to the last pbuf which initially is the first */ pcb->psize = 0; pcb->qsize = 0; } switch(payload[i]) { case PPP_END: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP_END i = %d pcb->psize = %d\n", i, pcb->psize)); if(pcb->psize > 0) { /* Packet received */ /* Do a frame check */ if((fcs16_crc_check(pcb->p, pcb->psize))) { /* Silently discard frame */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: FCS failed pcb->psize = %d\n", pcb->psize)); pbuf_free(pcb->p); pcb->p = NULL; break; } pbuf_realloc(pcb->p, pcb->psize - 2); /* Remove the FCS field and adjust packet size */ /* Have address and control field been compressed? */ if(ntohs(*((u16_t *)pcb->p->payload)) != PPP_ADDRCTRL) { if(!(pcb->lcpcfg & LCP_CFG_IN_ACCOMP)) { /* Silently discard frame */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Discard frame without address and control field when no compression negotiated i = %d pcb->psize = %d\n", i, pcb->psize)); pbuf_free(pcb->p); pcb->p = NULL; break; } } else { pbuf_header(pcb->p, -2); /* Hide address and control field */ } /* Check if least significant bit of octet is a '1' and if protocol compression is in use */ if(*((u8_t *)pcb->p->payload) & 1 && pcb->lcpcfg & LCP_CFG_IN_PCOMP) { proto = *((u8_t *)pcb->p->payload); pbuf_header(pcb->p, -1); } else { proto = ntohs(*((u16_t *)pcb->p->payload)); pbuf_header(pcb->p, -2); } /* Check that size of incoming packet does not exceed MRU */ if(pcb->p->tot_len > pcb->mru) { /* Silently discard frame */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Size of incoming packet exceed MRU, %d, %d\n", i, pcb->psize)); pbuf_free(pcb->p); pcb->p = NULL; break; } switch(proto) { //struct ppp_cp_hdr *cphdr; case PPP_LCP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP_LCP i = %d pcb->psize = %d code = 0x%x\n", i, pcb->psize, ((u8_t *)pcb->p)[0])); ppp_process_lcp(pcb, pcb->p); pbuf_free(pcb->p); pcb->p = NULL; break; case PPP_PAP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP_PAP i = %d pcb->psize = %d\n", i, pcb->psize)); ppp_process_pap(pcb, pcb->p); pbuf_free(pcb->p); pcb->p = NULL; break; case PPP_IPCP: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP_IPCP i = %d pcb->psize = %d\n", i, pcb->psize)); ppp_process_ipcp(pcb, pcb->p); pbuf_free(pcb->p); pcb->p = NULL; break; case PPP_IP_DATA: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP_IP_DATA i = %d pcb->psize = %d pcb->p->tot_len = %d\n", i, pcb->psize, pcb->p->tot_len)); for(r = pcb->p; r != NULL; r = r->next) { u16_t j; for(j = 0; j < r->len; ++j) { LWIP_DEBUGF(TCP_DEBUG, ("IN: 0x%x\n", ((u8_t *)r->payload)[j])); } LWIP_DEBUGF(TCP_DEBUG, ("\n")); } //for(r = pcb->p; r != NULL; r = r->next) { // u16_t j; // for(j = 0; j < r->len; ++j) { //LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: 0x%x\n", ((u8_t *)r->payload)[j])); // } //LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: r->len == %d r->tot_len == %d\n", r->len, r->tot_len)); //} PPP_EVENT_RECV(pcb,ret); pcb->p = NULL; /* Upper layer will dealloc pbuf. Delete any reference to it so we don't overwrite it */ break; default: LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Unknown protocol, proto == %d, %d, %d\n", proto, i, pcb->psize)); r = pbuf_alloc(PBUF_RAW, 2, PBUF_RAM); *((u16_t *)r->payload) = htons(proto); pbuf_chain(r, pcb->p); pbuf_free(pcb->p); pcb->p = NULL; ppp_cp_output(pcb, PPP_LCP, LCP_PROTO_REJ, ppp_next_id(), r); //cphdr = pcb->p->payload; //ppp_cp_output(pcb, proto, LCP_CFG_ACK, cphdr->id, NULL); break; } } break; case PPP_ESC: if(i < q->len-1) { ++i; /* Move to next element in buffer */ } else { if(q->next != NULL) { q = q->next; /* Move to next buffer in chain */ i = 0; payload = (u8_t *)q->payload; } else { /* Last character in receive buffer is an escape character */ //TODO: Is this a problem? LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Last character in receive buffer is an escape character!!!\n")); break; /* No more data in buffer */ } } payload[i] ^= 0x20; /* Character following an escape character is exclusive-ord with 0x20 */ /* FALLTHROUGH */ default: ((u8_t *)pcb->q->payload)[pcb->qsize] = payload[i]; ++pcb->psize; ++pcb->qsize; if(pcb->qsize == pcb->q->len) { /* Pbuf full. Allocate and add new tail to chain */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Pbuf full. Allocate and add new tail to chain\n")); pcb->qsize = 0; if((pcb->q = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL)) == NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: Could not allocate memory for pbuf pcb->q\n")); pbuf_free(p); return ERR_MEM; /* Could not allocate memory for pbuf */ } pbuf_chain(pcb->p, pcb->q); pbuf_free(pcb->q); } if(pcb->psize > PPP_MAX_SIZE) { /* PPP packet too big. Drop */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_input: PPP packet too big. Drop, i = %d pcb->psize = %d\n", i, pcb->psize)); pbuf_free(pcb->p); pcb->p = NULL; } break; } } } pbuf_free(p); return ERR_OK; } /*-----------------------------------------------------------------------------------*/ /* * ppp_new(): * * Creates a new PPP protocol control block but doesn't place it on * any of the PPP PCB lists. */ /*-----------------------------------------------------------------------------------*/ struct ppp_pcb * ppp_new(struct rfcomm_pcb *rfcommpcb) { struct ppp_pcb *pcb; pcb = lwbt_memp_malloc(MEMP_PPP_PCB); if(pcb != NULL) { memset(pcb, 0, sizeof(struct ppp_pcb)); pcb->rfcommpcb = rfcommpcb; pcb->state = PPP_LCP_CLOSED; pcb->mru = LCP_DEFAULT_MRU; pcb->outaccm = LCP_DEFAULT_ACCM; pcb->lcpopt = PPP_LCP_OPT; pcb->ipcpopt = PPP_IPCP_OPT; return pcb; } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_new: Could not allocate a new pcb\n")); return NULL; } /*-----------------------------------------------------------------------------------*/ /* * ppp_close(): * * Closes the PPP protocol control block. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_close(struct ppp_pcb *pcb) { struct ppp_req *req; err_t err; /* Remove any outstanding requests */ for(req = pcb->reqs; req != NULL; req = req->next) { PPP_REQ_RMV(&(pcb->reqs), req); if(req->p != NULL) { pbuf_free(req->p); } LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_close: Free memory for request with ID 0x%x***********\n", req->id)); lwbt_memp_free(MEMP_PPP_REQ, req); } /* Remove PCB from its list */ switch(pcb->state) { case PPP_LCP_LISTEN: PPP_RMV(&(ppp_listen_pcbs), pcb); break; default: PPP_RMV(&(ppp_active_pcbs), pcb); break; } err = ERR_OK; if(pcb->p != NULL) {//&& pcb->p->ref) { pbuf_free(pcb->p); /* Make sure we have freed any incoming packet */ pcb->p = NULL; } lwbt_memp_free(MEMP_PPP_PCB, pcb); pcb = NULL; return err; } /*-----------------------------------------------------------------------------------*/ /* * ppp_reset_all(): * * Closes all active PPP protocol control blocks. */ /*-----------------------------------------------------------------------------------*/ void ppp_reset_all(void) { struct ppp_pcb *pcb, *tpcb; for(pcb = ppp_active_pcbs; pcb != NULL;) { tpcb = pcb->next; ppp_close(pcb); pcb = tpcb; } for(pcb = ppp_listen_pcbs; pcb != NULL;) { tpcb = pcb->next; ppp_close(pcb); pcb = tpcb; } ppp_init(); } /*-----------------------------------------------------------------------------------*/ /* * lcp_cfg_req(): * * Assembles but do not send a LCP configuration request. The Options field is * filled with any desired changes to the link defaults. */ /*-----------------------------------------------------------------------------------*/ struct pbuf * lcp_cfg_req(struct ppp_pcb *pcb, struct pbuf *options) { struct pbuf *p, *q; struct ppp_cfg_hdr *cfghdr; p = NULL; if(pcb->lcpopt & LCP_CFG_OPT_MRU) { if(PPP_IN_MRU != LCP_DEFAULT_MRU) { p = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 2, PBUF_RAM); cfghdr = p->payload; cfghdr->type = LCP_CFG_MRU; cfghdr->len = p->len; ((u16_t *)p->payload)[1] = htons(PPP_IN_MRU); } } if(pcb->lcpopt & LCP_CFG_OPT_ACCM) { /* Add ACCM for outgoing packets */ if(PPP_ACCM != LCP_DEFAULT_ACCM) { q = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 4, PBUF_RAM); cfghdr = q->payload; cfghdr->type = LCP_CFG_ACCM; cfghdr->len = q->len; *((u32_t *)(((u16_t *)q->payload)+1)) = htonl(PPP_ACCM); if(p == NULL) { p = q; } else { pbuf_chain(p, q); pbuf_free(q); } } } if(pcb->lcpopt & LCP_CFG_OPT_PAP) { //TODO: IMPLEMENT PAP configuration request } #if PPP_PHDR_COMP if(pcb->lcpopt & LCP_CFG_OPT_PFC) { q = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN, PBUF_RAM); cfghdr = q->payload; cfghdr->type = LCP_CFG_P_COMP; cfghdr->len = q->len; if(p == NULL) { p = q; } else { pbuf_chain(p, q); pbuf_free(q); } } #endif /* PPP_PHDR_COMP */ #if PPP_ACHDR_COMP if(pcb->lcpopt & LCP_CFG_OPT_ACFC) { q = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN, PBUF_RAM); cfghdr = q->payload; cfghdr->type = LCP_CFG_AC_COMP; cfghdr->len = q->len; if(p == NULL) { p = q; } else { pbuf_chain(p, q); pbuf_free(q); } } #endif /* PPP_ACHDR_COMP */ if(options != NULL) { pbuf_chain(p, options); pbuf_free(options); } return p; } /*-----------------------------------------------------------------------------------*/ /* * ipcp_cfg_req(): * * Sends a IPCP configuration request to open an IPCP connection. The Options field * is filled with any desired changes to the link defaults. */ /*-----------------------------------------------------------------------------------*/ err_t ipcp_cfg_req(struct ppp_pcb *pcb) { struct pbuf *p = NULL; struct ppp_cfg_hdr *cfghdr; if(pcb->ipcpopt & IPCP_CFG_OPT_IP) { if((p = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 4, PBUF_RAM)) == NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ipcp_cfg_req: Could not allocate memory for pbuf\n")); return ERR_MEM; /* Could not allocate memory for pbuf */ } cfghdr = p->payload; cfghdr->type = IPCP_CFG_IPADDR; cfghdr->len = p->len; *((u32_t *)(((u16_t *)p->payload)+1)) = pcb->bluetoothif->ip_addr.addr; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ipcp_cfg_req: Local IP address: %d.%d.%d.%d\n", (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 24) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 16) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 8) & 0xff, (u8_t)ntohl(pcb->bluetoothif->ip_addr.addr) & 0xff)); } #if PPP_VJ_COMP if(pcb->ipcpopt & IPCP_CFG_OPT_VJ) { if((q = pbuf_alloc(PBUF_RAW, PPP_CFGHDR_LEN + 4, PBUF_RAM)) == NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ipcp_cfg_req: Could not allocate memory for pbuf\n")); return ERR_MEM; /* Could not allocate memory for pbuf */ } cfghdr = q->payload; cfghdr->type = IPCP_CFG_COMP; cfghdr->len = q->len; ((u16_t *)q->payload)[1] = PPP_VJ_TYPE; ((u8_t *)q->payload)[4] = PPP_MAXSLOT_ID; ((u8_t *)q->payload)[5] = PPP_COMPSLOT_ID; if(p == NULL) { p = q; } else { pbuf_chain(p, q); pbuf_free(q); } } #endif /* PPP_VJ_COMP */ return ppp_cp_output(pcb, PPP_IPCP, IPCP_CFG_REQ, ppp_next_id(), p); } /*-----------------------------------------------------------------------------------*/ /* * ppp_connect(): * * Initiates a connection attempt (LCP and IPCP) and specify the function to be * called when both LCP and IPCP has been connected. */ /*-----------------------------------------------------------------------------------*/ err_t //INITIATOR ppp_connect(struct ppp_pcb *pcb, err_t (* connected)(void *arg, struct ppp_pcb *tpcb, err_t err)) { struct pbuf *p; err_t ret = ERR_OK; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_connect\n")); pcb->connected = connected; pcb->pppcfg |= PPP_IR; /* We are the initiator */ if(pcb->state == PPP_LCP_OPEN) { if((ret = ipcp_cfg_req(pcb)) == ERR_OK) { pcb->state = PPP_IPCP_CFG; } } else { PPP_REG(&ppp_active_pcbs, pcb); p = lcp_cfg_req(pcb, NULL); if((ret = ppp_cp_output(pcb, PPP_LCP, LCP_CFG_REQ, ppp_next_id(), p)) == ERR_OK) { pcb->state = PPP_LCP_CFG; } } return ret; } /*-----------------------------------------------------------------------------------*/ /* * ppp_disconnect(): * * Closes the connection held by the PCB but does not dealloc it. Call the * disconnected callback when disconnection is complete. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_disconnect(struct ppp_pcb *pcb) { err_t ret; if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CLOSING) { PPP_EVENT_DISCONNECTED(pcb, ERR_OK, PPP_LCP, ret); return ERR_OK; } pcb->state = PPP_LCP_CLOSING; return ppp_cp_output(pcb, PPP_LCP, LCP_TERM_REQ, ppp_next_id(), NULL); } /*-----------------------------------------------------------------------------------*/ /* * ppp_echo(): * * Sends an empty PPP echo request message. Also specify the function that should * be called when a PPP echo reply has been received. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_echo(struct ppp_pcb *pcb, err_t (* echo_rsp)(void *arg, struct ppp_pcb *tpcb, err_t err)) { if(pcb->state == PPP_LCP_CLOSED || pcb->state == PPP_LCP_LISTEN || pcb->state == PPP_LCP_CFG || pcb->state == PPP_LCP_CLOSING) { return ERR_CLSD; } pcb->echo_rsp = echo_rsp; return ppp_cp_output(pcb, PPP_LCP, LCP_ECHO_REQ, ppp_next_id(), NULL); } /*-----------------------------------------------------------------------------------*/ /* * ppp_cp_output(): * * Sends a PPP control protocol packet (IPCP or LCP). */ /*-----------------------------------------------------------------------------------*/ err_t ppp_cp_output(struct ppp_pcb *pcb, u16_t proto, u8_t code, u8_t id, struct pbuf *q) { struct pbuf *p, *r; struct ppp_req *req; err_t ret; u16_t fcs; u8_t j = 0; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_cp_output: Code == 0x%x\n", code)); if(pcb->lcpcfg & LCP_CFG_OUT_ACCOMP && pcb->state != PPP_LCP_CFG) { p = pbuf_alloc(PBUF_RAW, PPP_HDR_LEN - 2 + PPP_CPHDR_LEN, PBUF_RAM); } else { p = pbuf_alloc(PBUF_RAW, PPP_HDR_LEN + PPP_CPHDR_LEN, PBUF_RAM); } if(!(pcb->lcpcfg & LCP_CFG_OUT_ACCOMP && pcb->state != PPP_LCP_CFG)) { ((u16_t *)p->payload)[j/2] = htons(PPP_ADDRCTRL); /* Set adddress control field */ j+= 2; } ((u16_t *)p->payload)[j/2] = htons(proto); /* Set protocol field */ j+= 2; ((u8_t *)p->payload)[j++] = code; /* Set type of control packet */ ((u8_t *)p->payload)[j++] = id; /* Set packet identifier */ if(q == NULL) { ((u16_t *)p->payload)[j/2] = htons(PPP_CPHDR_LEN); /* Set length of control packet */ j+= 2; } else { ((u16_t *)p->payload)[j/2] = htons(q->tot_len + PPP_CPHDR_LEN); /* Set length of control packet */ j+= 2; pbuf_chain(p, q); pbuf_free(q); } r = pbuf_alloc(PBUF_RAW, 2, PBUF_RAM); /* Alloc a pbuf for fcs */ /* Add FCS to packet */ fcs = fcs16_crc_calc(p, p->tot_len); ((u8_t *)r->payload)[0] = (fcs & 0xFF); /* Least significant byte first */ ((u8_t *)r->payload)[1] = ((fcs >> 8) & 0xFF); pbuf_chain(p, r); pbuf_free(r); ret = ppp_output(pcb, p); /* Check if a timer should be associated with the request */ if(ret == ERR_OK && (code == LCP_CFG_REQ || code == IPCP_CFG_REQ || code == LCP_TERM_REQ || code == IPCP_TERM_REQ)) { /* Check if the request already is in the outstanding requests list */ for(req = pcb->reqs; req != NULL; req = req->next) { if(req->id == id) { break; } } if(req == NULL) { /* Add request to outstanding requests list */ LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_cp_output: Allocate memory for request with ID 0x%x Code 0x%x***********\n", id, code)); if((req = lwbt_memp_malloc(MEMP_PPP_REQ)) == NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_cp_output: could not allocate memory for request\n")); pbuf_free(p); /* Dealloc control packet */ return ERR_MEM; } req->p = p; req->id = id; req->proto = proto; if((req->rto = (PPP_RTO/(PPP_NRTX + 1))) == 0) { req->rto = 1; } req->nrtx = PPP_NRTX; PPP_REQ_REG(&(pcb->reqs), req); } } else { pbuf_free(p); /* Free control packet */ } return ret; } /*-----------------------------------------------------------------------------------*/ /* * ppp_netif_output(): * * Output IP data from a lwIP network interface over PPP. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_netif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) { struct ppp_pcb *pcb; /* struct pbuf *q; u16_t i; for(q = p; q != NULL; q = q->next) { for(i = 0; i < q->len; ++i) { LWIP_DEBUGF(TCP_DEBUG, ("Out: 0x%x\n", ((u8_t *)q->payload)[i])); } LWIP_DEBUGF(TCP_DEBUG, ("\n")); } */ for(pcb = ppp_active_pcbs; pcb != NULL; pcb = pcb->next) { if(pcb->bluetoothif == netif) { break; } } if(pcb != NULL) { LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_netif_output: Local IP address: %d.%d.%d.%d\n", (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 24) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 16) & 0xff, (u8_t)(ntohl(pcb->bluetoothif->ip_addr.addr) >> 8) & 0xff, (u8_t)ntohl(pcb->bluetoothif->ip_addr.addr) & 0xff)); return ppp_data_output(pcb, p); } return ERR_CONN; /* No matching PPP connection exists */ } /*-----------------------------------------------------------------------------------*/ /* * ppp_data_output(): * * Output IP data over PPP. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_data_output(struct ppp_pcb *pcb, struct pbuf *q) { struct pbuf *r, *p; u8_t hdrlen = PPP_HDR_LEN; u8_t i = 0; err_t ret; u16_t fcs; LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_data_output: q->tot_len = %d\n", q->tot_len)); if(pcb == NULL) { return ERR_CONN; /* Not connected */ } if(pcb->state != PPP_IPCP_OPEN) { return ERR_CONN; /* Not connected */ } if(pcb->lcpcfg & LCP_CFG_OUT_ACCOMP) { hdrlen -= 2; } if(pcb->lcpcfg & LCP_CFG_OUT_PCOMP) { --hdrlen; } p = pbuf_alloc(PBUF_RAW, hdrlen, PBUF_RAM); if(!(pcb->lcpcfg & LCP_CFG_OUT_ACCOMP)) { *((u16_t *)p->payload) = htons(PPP_ADDRCTRL); i += 2; } if(pcb->lcpcfg & LCP_CFG_OUT_PCOMP) { ((u8_t *)p->payload)[i] = PPP_IP_DATA; } else { ((u16_t *)p->payload)[i/2] = htons(PPP_IP_DATA); //TODO: WE MAY DIVIDE BY ZERO HERE... } /* Chain any information data to header */ if(q != NULL) { pbuf_chain(p, q); //pbuf_free(q); } r = pbuf_alloc(PBUF_RAW, 2, PBUF_RAM); /* Alloc a pbuf for fcs */ /* Add FCS to packet */ fcs = fcs16_crc_calc(p, p->tot_len); ((u8_t *)r->payload)[0] = (fcs & 0x00ff); /* Least significant byte first */ ((u8_t *)r->payload)[1] = ((fcs >> 8) & 0x00ff); pbuf_chain(p, r); pbuf_free(r); ret = ppp_output(pcb, p); /* Free PPP header. Higher layers will handle rest of packet */ if(q != NULL) { pbuf_dechain(p); pbuf_realloc(q, q->tot_len-2); /* Remove FCS from packet */ } pbuf_free(p); return ret; } /*-----------------------------------------------------------------------------------*/ /* * ppp_output_alloc(): * * Called by ppp_output() to allocate a pbuf with the same size as the remaining * characters in the packet to be byte stuffed. */ /*-----------------------------------------------------------------------------------*/ struct pbuf * ppp_output_alloc(struct pbuf *p, u16_t i) { struct pbuf *q; u16_t k = 0; /* Get length of remaining characters in packet */ for(q = p; q != NULL; q = q->next) { k += q->len; } k -= i; q = pbuf_alloc(PBUF_RAW, k, PBUF_RAM); /* Alloc a pbuf for that size */ return q; } /*-----------------------------------------------------------------------------------*/ /* * ppp_output(): * * Output PPP data. Byte stuffs the packet and forward it to RFCOMM. */ /*-----------------------------------------------------------------------------------*/ err_t ppp_output(struct ppp_pcb *pcb, struct pbuf *p) { struct pbuf *q, *r, *s; u16_t i; u16_t j = 0; u8_t c; err_t ret; /* To minimize the number of pbufs we need to chain together we always allocate the same size as the number of characters remaining to be checked */ r = pbuf_alloc(PBUF_RAW, p->tot_len + 1, PBUF_RAM); s = r; ((u8_t *)s->payload)[j++] = PPP_END; for(q = p; q != NULL; q = q->next) { for(i = 0; i < q->len; ++i) { c = ((u8_t *)q->payload)[i]; if(j == s->len) { s = ppp_output_alloc(q, i); pbuf_chain(r, s); pbuf_free(s); j = 0; } switch(c) { case PPP_END: ((u8_t *)s->payload)[j++] = PPP_ESC; if(j == s->len) { s = ppp_output_alloc(q, i); pbuf_chain(r, s); pbuf_free(s); j = 0; } ((u8_t *)s->payload)[j++] = PPP_ESC_END; break; case PPP_ESC: ((u8_t *)s->payload)[j++] = PPP_ESC; if(j == s->len) { s = ppp_output_alloc(q, i); pbuf_chain(r, s); pbuf_free(s); j = 0; } ((u8_t *)s->payload)[j++] = PPP_ESC_ESC; break; default: /* Check if the character should be escaped according to the ACCM */ if(c < 0x20) { if(pcb->outaccm & (1 << c) || pcb->state == PPP_LCP_CFG) { ((u8_t *)s->payload)[j++] = PPP_ESC; if(j == s->len) { s = ppp_output_alloc(q, i); pbuf_chain(r, s); pbuf_free(s); j = 0; } ((u8_t *)s->payload)[j++] = c ^ 0x20; /* Character following escape character is exclusive-ord with 0x20 */ } else { ((u8_t *)s->payload)[j++] = c; } } else { ((u8_t *)s->payload)[j++] = c; } break; } } } s = pbuf_alloc(PBUF_RAW, 1, PBUF_RAM); /* Alloc a pbuf for PPP_END */ *((u8_t *)s->payload) = PPP_END; pbuf_chain(r, s); pbuf_free(s); LWIP_DEBUGF(LWBT_PPP_DEBUG, ("ppp_output: r->tot_len = %d\n", r->tot_len)); /* for(s = r; s != NULL; s = s->next) { //u8_t i; for(i = 0; i < s->len; ++i) { LWIP_DEBUGF(SDP_DEBUG, ("ppp_output: REQ 0x%x\n", ((u8_t *)s->payload)[i])); } LWIP_DEBUGF(SDP_DEBUG, ("ppp_output: STOP\n")); } */ /* Check which convergence layer is in use */ if(rfcomm_cl(pcb->rfcommpcb)) { ret = rfcomm_uih_credits(pcb->rfcommpcb, PBUF_POOL_SIZE - rfcomm_remote_credits(pcb->rfcommpcb), r); } else { ret = rfcomm_uih(pcb->rfcommpcb, rfcomm_cn(pcb->rfcommpcb), r); } pbuf_free(r); /* Free byte stuffed copy of outgoing packet */ return ret; } /*-----------------------------------------------------------------------------------*/ /* * ppp_next_id(): * * Return a new control protocol identifier. Aids in matching requests and replies. */ /*-----------------------------------------------------------------------------------*/ u8_t ppp_next_id(void) { return ++id_nxt; } /*-----------------------------------------------------------------------------------*/ /* * ppp_disconnected(): * * Used to specify the function that should be called when a PPP connection is * disconnected */ /*-----------------------------------------------------------------------------------*/ void ppp_disconnected(struct ppp_pcb *pcb, err_t (* disconnected)(void *arg, struct ppp_pcb *pcb, u16_t proto, err_t err)) { pcb->disconnected = disconnected; } /*-----------------------------------------------------------------------------------*/ /* * ppp_listen(): * * Set the state of the connection to be PPP_LCP_LISTEN, which means that it is able * to accept incoming connections. */ /*-----------------------------------------------------------------------------------*/ #if LWBT_LAP err_t ppp_listen(struct ppp_pcb *pcb, err_t (* accept)(void *arg, struct ppp_pcb *pcb, err_t err)) { pcb->connected = accept; pcb->state = PPP_LCP_LISTEN; PPP_REG(&ppp_listen_pcbs, pcb); return ERR_OK; } #endif /* LWBT_LAP */ /*-----------------------------------------------------------------------------------*/ /* * ppp_netif(): * * Used to specify the network interface that should be used to pass incoming IP * data to lwIP. */ /*-----------------------------------------------------------------------------------*/ void ppp_netif(struct ppp_pcb *pcb, struct netif *netif) { pcb->bluetoothif = netif; } /*-----------------------------------------------------------------------------------*/ /* * ppp_arg(): * * Used to specify the argument that should be passed callback functions. */ /*-----------------------------------------------------------------------------------*/ void ppp_arg(struct ppp_pcb *pcb, void *arg) { pcb->callback_arg = arg; } /*-----------------------------------------------------------------------------------*/ void ppp_pbuf_ref_chain(struct pbuf *p) { while (p != NULL) { ++p->ref; p = p->next; } } /*-----------------------------------------------------------------------------------*/