From 706e567fc5ff6b79738a5f470e5aa7b2cae76459 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Thu, 2 Jun 2011 14:26:57 -0700 Subject: libppp: import user space PPP implementation from FreeBSD 7.4-RELEASE. Change-Id: I78d2eb0fa010078b4cd131cadc39bf32cbc93986 --- src/cbcp.c | 763 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 763 insertions(+) create mode 100644 src/cbcp.c (limited to 'src/cbcp.c') diff --git a/src/cbcp.c b/src/cbcp.c new file mode 100644 index 0000000..53cebc8 --- /dev/null +++ b/src/cbcp.c @@ -0,0 +1,763 @@ +/*- + * Copyright (c) 1998 Brian Somers + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/usr.sbin/ppp/cbcp.c,v 1.26.26.1 2010/12/21 17:10:29 kensmith Exp $ + */ + +#include + +#ifdef __FreeBSD__ +#include +#endif +#include + +#include +#include + +#include "layer.h" +#include "defs.h" +#include "log.h" +#include "timer.h" +#include "descriptor.h" +#include "lqr.h" +#include "mbuf.h" +#include "fsm.h" +#include "throughput.h" +#include "hdlc.h" +#include "lcp.h" +#include "ccp.h" +#include "link.h" +#include "async.h" +#include "physical.h" +#include "proto.h" +#include "cbcp.h" +#include "mp.h" +#include "chat.h" +#include "auth.h" +#include "chap.h" +#include "datalink.h" + +void +cbcp_Init(struct cbcp *cbcp, struct physical *p) +{ + cbcp->required = 0; + cbcp->fsm.state = CBCP_CLOSED; + cbcp->fsm.id = 0; + cbcp->fsm.delay = 0; + *cbcp->fsm.phone = '\0'; + memset(&cbcp->fsm.timer, '\0', sizeof cbcp->fsm.timer); + cbcp->p = p; +} + +static void cbcp_SendReq(struct cbcp *); +static void cbcp_SendResponse(struct cbcp *); +static void cbcp_SendAck(struct cbcp *); + +static void +cbcp_Timeout(void *v) +{ + struct cbcp *cbcp = (struct cbcp *)v; + + timer_Stop(&cbcp->fsm.timer); + if (cbcp->fsm.restart) { + switch (cbcp->fsm.state) { + case CBCP_CLOSED: + case CBCP_STOPPED: + log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n", + cbcp->p->dl->name); + break; + + case CBCP_REQSENT: + cbcp_SendReq(cbcp); + break; + case CBCP_RESPSENT: + cbcp_SendResponse(cbcp); + break; + case CBCP_ACKSENT: + cbcp_SendAck(cbcp); + break; + } + } else { + const char *missed; + + switch (cbcp->fsm.state) { + case CBCP_STOPPED: + missed = "REQ"; + break; + case CBCP_REQSENT: + missed = "RESPONSE"; + break; + case CBCP_RESPSENT: + missed = "ACK"; + break; + case CBCP_ACKSENT: + missed = "Terminate REQ"; + break; + default: + log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n", + cbcp->p->dl->name); + missed = NULL; + break; + } + if (missed) + log_Printf(LogCBCP, "%s: Timeout waiting for peer %s\n", + cbcp->p->dl->name, missed); + datalink_CBCPFailed(cbcp->p->dl); + } +} + +static void +cbcp_StartTimer(struct cbcp *cbcp, int timeout) +{ + timer_Stop(&cbcp->fsm.timer); + cbcp->fsm.timer.func = cbcp_Timeout; + cbcp->fsm.timer.name = "cbcp"; + cbcp->fsm.timer.load = timeout * SECTICKS; + cbcp->fsm.timer.arg = cbcp; + timer_Start(&cbcp->fsm.timer); +} + +#define CBCP_CLOSED (0) /* Not in use */ +#define CBCP_STOPPED (1) /* Waiting for a REQ */ +#define CBCP_REQSENT (2) /* Waiting for a RESP */ +#define CBCP_RESPSENT (3) /* Waiting for an ACK */ +#define CBCP_ACKSENT (4) /* Waiting for an LCP Term REQ */ + +static const char * const cbcpname[] = { + "closed", "stopped", "req-sent", "resp-sent", "ack-sent" +}; + +static const char * +cbcpstate(unsigned s) +{ + if (s < sizeof cbcpname / sizeof cbcpname[0]) + return cbcpname[s]; + return HexStr(s, NULL, 0); +} + +static void +cbcp_NewPhase(struct cbcp *cbcp, int new) +{ + if (cbcp->fsm.state != new) { + log_Printf(LogCBCP, "%s: State change %s --> %s\n", cbcp->p->dl->name, + cbcpstate(cbcp->fsm.state), cbcpstate(new)); + cbcp->fsm.state = new; + } +} + +struct cbcp_header { + u_char code; + u_char id; + u_int16_t length; /* Network byte order */ +}; + + +/* cbcp_header::code values */ +#define CBCP_REQ (1) +#define CBCP_RESPONSE (2) +#define CBCP_ACK (3) + +struct cbcp_data { + u_char type; + u_char length; + u_char delay; + char addr_start[253]; /* max cbcp_data length 255 + 1 for NULL */ +}; + +/* cbcp_data::type values */ +#define CBCP_NONUM (1) +#define CBCP_CLIENTNUM (2) +#define CBCP_SERVERNUM (3) +#define CBCP_LISTNUM (4) + +static void +cbcp_Output(struct cbcp *cbcp, u_char code, struct cbcp_data *data) +{ + struct cbcp_header *head; + struct mbuf *bp; + + bp = m_get(sizeof *head + data->length, MB_CBCPOUT); + head = (struct cbcp_header *)MBUF_CTOP(bp); + head->code = code; + head->id = cbcp->fsm.id; + head->length = htons(sizeof *head + data->length); + memcpy(MBUF_CTOP(bp) + sizeof *head, data, data->length); + log_DumpBp(LogDEBUG, "cbcp_Output", bp); + link_PushPacket(&cbcp->p->link, bp, cbcp->p->dl->bundle, + LINK_QUEUES(&cbcp->p->link) - 1, PROTO_CBCP); +} + +static const char * +cbcp_data_Type(unsigned type) +{ + static const char * const types[] = { + "No callback", "User-spec", "Server-spec", "list" + }; + + if (type < 1 || type > sizeof types / sizeof types[0]) + return HexStr(type, NULL, 0); + return types[type-1]; +} + +struct cbcp_addr { + u_char type; + char addr[sizeof ((struct cbcp_data *)0)->addr_start - 1]; /* ASCIIZ */ +}; + +/* cbcp_data::type values */ +#define CBCP_ADDR_PSTN (1) + +static void +cbcp_data_Show(struct cbcp_data *data) +{ + struct cbcp_addr *addr; + char *end; + + addr = (struct cbcp_addr *)data->addr_start; + end = (char *)data + data->length; + *end = '\0'; + + log_Printf(LogCBCP, " TYPE %s\n", cbcp_data_Type(data->type)); + if ((char *)&data->delay < end) { + log_Printf(LogCBCP, " DELAY %d\n", data->delay); + while (addr->addr < end) { + if (addr->type == CBCP_ADDR_PSTN) + log_Printf(LogCBCP, " ADDR %s\n", addr->addr); + else + log_Printf(LogCBCP, " ADDR type %d ??\n", (int)addr->type); + addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1); + } + } +} + +static void +cbcp_SendReq(struct cbcp *cbcp) +{ + struct cbcp_data data; + struct cbcp_addr *addr; + char list[sizeof cbcp->fsm.phone], *next; + int len, max; + + /* Only callees send REQs */ + + log_Printf(LogCBCP, "%s: SendReq(%d) state = %s\n", cbcp->p->dl->name, + cbcp->fsm.id, cbcpstate(cbcp->fsm.state)); + data.type = cbcp->fsm.type; + data.delay = 0; + strncpy(list, cbcp->fsm.phone, sizeof list - 1); + list[sizeof list - 1] = '\0'; + + switch (data.type) { + case CBCP_CLIENTNUM: + addr = (struct cbcp_addr *)data.addr_start; + addr->type = CBCP_ADDR_PSTN; + *addr->addr = '\0'; + data.length = addr->addr - (char *)&data; + break; + + case CBCP_LISTNUM: + addr = (struct cbcp_addr *)data.addr_start; + for (next = strtok(list, ","); next; next = strtok(NULL, ",")) { + len = strlen(next); + max = data.addr_start + sizeof data.addr_start - addr->addr - 1; + if (len <= max) { + addr->type = CBCP_ADDR_PSTN; + strncpy(addr->addr, next, sizeof addr->addr - 1); + addr->addr[sizeof addr->addr - 1] = '\0'; + addr = (struct cbcp_addr *)((char *)addr + len + 2); + } else + log_Printf(LogWARN, "CBCP ADDR \"%s\" skipped - packet too large\n", + next); + } + data.length = (char *)addr - (char *)&data; + break; + + case CBCP_SERVERNUM: + data.length = data.addr_start - (char *)&data; + break; + + default: + data.length = (char *)&data.delay - (char *)&data; + break; + } + + cbcp_data_Show(&data); + cbcp_Output(cbcp, CBCP_REQ, &data); + cbcp->fsm.restart--; + cbcp_StartTimer(cbcp, cbcp->fsm.delay); + cbcp_NewPhase(cbcp, CBCP_REQSENT); /* Wait for a RESPONSE */ +} + +void +cbcp_Up(struct cbcp *cbcp) +{ + struct lcp *lcp = &cbcp->p->link.lcp; + + cbcp->fsm.delay = cbcp->p->dl->cfg.cbcp.delay; + if (*cbcp->p->dl->peer.authname == '\0' || + !auth_SetPhoneList(cbcp->p->dl->peer.authname, cbcp->fsm.phone, + sizeof cbcp->fsm.phone)) { + strncpy(cbcp->fsm.phone, cbcp->p->dl->cfg.cbcp.phone, + sizeof cbcp->fsm.phone - 1); + cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0'; + } + + if (lcp->want_callback.opmask) { + if (*cbcp->fsm.phone == '\0') + cbcp->fsm.type = CBCP_NONUM; + else if (!strcmp(cbcp->fsm.phone, "*")) { + cbcp->fsm.type = CBCP_SERVERNUM; + *cbcp->fsm.phone = '\0'; + } else + cbcp->fsm.type = CBCP_CLIENTNUM; + cbcp_NewPhase(cbcp, CBCP_STOPPED); /* Wait for a REQ */ + cbcp_StartTimer(cbcp, cbcp->fsm.delay * DEF_FSMTRIES); + } else { + if (*cbcp->fsm.phone == '\0') + cbcp->fsm.type = CBCP_NONUM; + else if (!strcmp(cbcp->fsm.phone, "*")) { + cbcp->fsm.type = CBCP_CLIENTNUM; + *cbcp->fsm.phone = '\0'; + } else if (strchr(cbcp->fsm.phone, ',')) + cbcp->fsm.type = CBCP_LISTNUM; + else + cbcp->fsm.type = CBCP_SERVERNUM; + cbcp->fsm.restart = DEF_FSMTRIES; + cbcp_SendReq(cbcp); + } +} + +static int +cbcp_AdjustResponse(struct cbcp *cbcp, struct cbcp_data *data) +{ + /* + * We've received a REQ (data). Adjust our reponse (cbcp->fsm.*) + * so that we (hopefully) agree with the peer + */ + struct cbcp_addr *addr; + + switch (data->type) { + case CBCP_NONUM: + if (cbcp->p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) + /* + * if ``none'' is a configured callback possibility + * (ie, ``set callback cbcp none''), go along with the callees + * request + */ + cbcp->fsm.type = CBCP_NONUM; + + /* + * Otherwise, we send our desired response anyway. This seems to be + * what Win95 does - although I can't find this behaviour documented + * in the CBCP spec.... + */ + + return 1; + + case CBCP_CLIENTNUM: + if (cbcp->fsm.type == CBCP_CLIENTNUM) { + char *ptr; + + if (data->length > data->addr_start - (char *)data) { + /* + * The peer has given us an address type spec - make sure we + * understand ! + */ + addr = (struct cbcp_addr *)data->addr_start; + if (addr->type != CBCP_ADDR_PSTN) { + log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n", + (int)addr->type); + return 0; + } + } + /* we accept the REQ even if the peer didn't specify an addr->type */ + ptr = strchr(cbcp->fsm.phone, ','); + if (ptr) + *ptr = '\0'; /* Just use the first number in our list */ + return 1; + } + log_Printf(LogPHASE, "CBCP: no number to pass to the peer !\n"); + return 0; + + case CBCP_SERVERNUM: + if (cbcp->fsm.type == CBCP_SERVERNUM) { + *cbcp->fsm.phone = '\0'; + return 1; + } + if (data->length > data->addr_start - (char *)data) { + /* + * This violates the spec, but if the peer has told us the + * number it wants to call back, take advantage of this fact + * and allow things to proceed if we've specified the same + * number + */ + addr = (struct cbcp_addr *)data->addr_start; + if (addr->type != CBCP_ADDR_PSTN) { + log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n", + (int)addr->type); + return 0; + } else if (cbcp->fsm.type == CBCP_CLIENTNUM) { + /* + * If the peer's insisting on deciding the number, make sure + * it's one of the ones in our list. If it is, let the peer + * think it's in control :-) + */ + char list[sizeof cbcp->fsm.phone], *next; + + strncpy(list, cbcp->fsm.phone, sizeof list - 1); + list[sizeof list - 1] = '\0'; + for (next = strtok(list, ","); next; next = strtok(NULL, ",")) + if (!strcmp(next, addr->addr)) { + cbcp->fsm.type = CBCP_SERVERNUM; + strcpy(cbcp->fsm.phone, next); + return 1; + } + } + } + log_Printf(LogPHASE, "CBCP: Peer won't allow local decision !\n"); + return 0; + + case CBCP_LISTNUM: + if (cbcp->fsm.type == CBCP_CLIENTNUM || cbcp->fsm.type == CBCP_LISTNUM) { + /* + * Search through ``data''s addresses and see if cbcp->fsm.phone + * contains any of them + */ + char list[sizeof cbcp->fsm.phone], *next, *end; + + addr = (struct cbcp_addr *)data->addr_start; + end = (char *)data + data->length; + + while (addr->addr < end) { + if (addr->type == CBCP_ADDR_PSTN) { + strncpy(list, cbcp->fsm.phone, sizeof list - 1); + list[sizeof list - 1] = '\0'; + for (next = strtok(list, ","); next; next = strtok(NULL, ",")) + if (!strcmp(next, addr->addr)) { + cbcp->fsm.type = CBCP_LISTNUM; + strcpy(cbcp->fsm.phone, next); + return 1; + } + } else + log_Printf(LogCBCP, "Warning: Unrecognised address type %d !\n", + (int)addr->type); + addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1); + } + } + log_Printf(LogPHASE, "CBCP: no good number to pass to the peer !\n"); + return 0; + } + + log_Printf(LogCBCP, "Unrecognised REQ type %d !\n", (int)data->type); + return 0; +} + +static void +cbcp_SendResponse(struct cbcp *cbcp) +{ + struct cbcp_data data; + struct cbcp_addr *addr; + + /* Only callers send RESPONSEs */ + + log_Printf(LogCBCP, "%s: SendResponse(%d) state = %s\n", cbcp->p->dl->name, + cbcp->fsm.id, cbcpstate(cbcp->fsm.state)); + + data.type = cbcp->fsm.type; + data.delay = cbcp->fsm.delay; + addr = (struct cbcp_addr *)data.addr_start; + if (data.type == CBCP_NONUM) + data.length = (char *)&data.delay - (char *)&data; + else if (*cbcp->fsm.phone) { + addr->type = CBCP_ADDR_PSTN; + strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1); + addr->addr[sizeof addr->addr - 1] = '\0'; + data.length = (addr->addr + strlen(addr->addr) + 1) - (char *)&data; + } else + data.length = data.addr_start - (char *)&data; + + cbcp_data_Show(&data); + cbcp_Output(cbcp, CBCP_RESPONSE, &data); + cbcp->fsm.restart--; + cbcp_StartTimer(cbcp, cbcp->fsm.delay); + cbcp_NewPhase(cbcp, CBCP_RESPSENT); /* Wait for an ACK */ +} + +/* What to do after checking an incoming response */ +#define CBCP_ACTION_DOWN (0) +#define CBCP_ACTION_REQ (1) +#define CBCP_ACTION_ACK (2) + +static int +cbcp_CheckResponse(struct cbcp *cbcp, struct cbcp_data *data) +{ + /* + * We've received a RESPONSE (data). Check if it agrees with + * our REQ (cbcp->fsm) + */ + struct cbcp_addr *addr; + + addr = (struct cbcp_addr *)data->addr_start; + + if (data->type == cbcp->fsm.type) { + switch (cbcp->fsm.type) { + case CBCP_NONUM: + return CBCP_ACTION_ACK; + + case CBCP_CLIENTNUM: + if ((char *)data + data->length <= addr->addr) + log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n"); + else if (addr->type != CBCP_ADDR_PSTN) + log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n", + addr->type); + else { + strncpy(cbcp->fsm.phone, addr->addr, sizeof cbcp->fsm.phone - 1); + cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0'; + cbcp->fsm.delay = data->delay; + return CBCP_ACTION_ACK; + } + return CBCP_ACTION_DOWN; + + case CBCP_SERVERNUM: + cbcp->fsm.delay = data->delay; + return CBCP_ACTION_ACK; + + case CBCP_LISTNUM: + if ((char *)data + data->length <= addr->addr) + log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n"); + else if (addr->type != CBCP_ADDR_PSTN) + log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n", + addr->type); + else { + char list[sizeof cbcp->fsm.phone], *next; + + strncpy(list, cbcp->fsm.phone, sizeof list - 1); + list[sizeof list - 1] = '\0'; + for (next = strtok(list, ","); next; next = strtok(NULL, ",")) + if (!strcmp(addr->addr, next)) { + strcpy(cbcp->fsm.phone, next); + cbcp->fsm.delay = data->delay; + return CBCP_ACTION_ACK; + } + log_Printf(LogPHASE, "CBCP: peer didn't respond with a " + "valid number !\n"); + } + return CBCP_ACTION_DOWN; + } + log_Printf(LogPHASE, "Internal CBCP error - agreed on %d !\n", + (int)cbcp->fsm.type); + return CBCP_ACTION_DOWN; + } else if (data->type == CBCP_NONUM && cbcp->fsm.type == CBCP_CLIENTNUM) { + /* + * Client doesn't want CBCP after all.... + * We only allow this when ``set cbcp *'' has been specified. + */ + cbcp->fsm.type = CBCP_NONUM; + return CBCP_ACTION_ACK; + } + log_Printf(LogCBCP, "Invalid peer RESPONSE\n"); + return CBCP_ACTION_REQ; +} + +static void +cbcp_SendAck(struct cbcp *cbcp) +{ + struct cbcp_data data; + struct cbcp_addr *addr; + + /* Only callees send ACKs */ + + log_Printf(LogCBCP, "%s: SendAck(%d) state = %s\n", cbcp->p->dl->name, + cbcp->fsm.id, cbcpstate(cbcp->fsm.state)); + + data.type = cbcp->fsm.type; + switch (data.type) { + case CBCP_NONUM: + data.length = (char *)&data.delay - (char *)&data; + break; + case CBCP_CLIENTNUM: + addr = (struct cbcp_addr *)data.addr_start; + addr->type = CBCP_ADDR_PSTN; + strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1); + addr->addr[sizeof addr->addr - 1] = '\0'; + data.delay = cbcp->fsm.delay; + data.length = addr->addr + strlen(addr->addr) + 1 - (char *)&data; + break; + default: + data.delay = cbcp->fsm.delay; + data.length = data.addr_start - (char *)&data; + break; + } + + cbcp_data_Show(&data); + cbcp_Output(cbcp, CBCP_ACK, &data); + cbcp->fsm.restart--; + cbcp_StartTimer(cbcp, cbcp->fsm.delay); + cbcp_NewPhase(cbcp, CBCP_ACKSENT); /* Wait for an ACK */ +} + +extern struct mbuf * +cbcp_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp) +{ + struct physical *p = link2physical(l); + struct cbcp_header *head; + struct cbcp_data *data; + struct cbcp *cbcp = &p->dl->cbcp; + size_t len; + + if (p == NULL) { + log_Printf(LogERROR, "cbcp_Input: Not a physical link - dropped\n"); + m_freem(bp); + return NULL; + } + + bp = m_pullup(bp); + len = m_length(bp); + if (len < sizeof(struct cbcp_header)) { + m_freem(bp); + return NULL; + } + head = (struct cbcp_header *)MBUF_CTOP(bp); + if (ntohs(head->length) != len) { + log_Printf(LogWARN, "Corrupt CBCP packet (code %d, length %u not %zu)" + " - ignored\n", head->code, ntohs(head->length), len); + m_freem(bp); + return NULL; + } + m_settype(bp, MB_CBCPIN); + + /* XXX check the id */ + + bp->m_offset += sizeof(struct cbcp_header); + bp->m_len -= sizeof(struct cbcp_header); + data = (struct cbcp_data *)MBUF_CTOP(bp); + + switch (head->code) { + case CBCP_REQ: + log_Printf(LogCBCP, "%s: RecvReq(%d) state = %s\n", + p->dl->name, head->id, cbcpstate(cbcp->fsm.state)); + cbcp_data_Show(data); + if (cbcp->fsm.state == CBCP_STOPPED || cbcp->fsm.state == CBCP_RESPSENT) { + timer_Stop(&cbcp->fsm.timer); + if (cbcp_AdjustResponse(cbcp, data)) { + cbcp->fsm.restart = DEF_FSMTRIES; + cbcp->fsm.id = head->id; + cbcp_SendResponse(cbcp); + } else + datalink_CBCPFailed(cbcp->p->dl); + } else + log_Printf(LogCBCP, "%s: unexpected REQ dropped\n", p->dl->name); + break; + + case CBCP_RESPONSE: + log_Printf(LogCBCP, "%s: RecvResponse(%d) state = %s\n", + p->dl->name, head->id, cbcpstate(cbcp->fsm.state)); + cbcp_data_Show(data); + if (cbcp->fsm.id != head->id) { + log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n", + cbcp->fsm.id, head->id); + cbcp->fsm.id = head->id; + } + if (cbcp->fsm.state == CBCP_REQSENT || cbcp->fsm.state == CBCP_ACKSENT) { + timer_Stop(&cbcp->fsm.timer); + switch (cbcp_CheckResponse(cbcp, data)) { + case CBCP_ACTION_REQ: + cbcp_SendReq(cbcp); + break; + + case CBCP_ACTION_ACK: + cbcp->fsm.restart = DEF_FSMTRIES; + cbcp_SendAck(cbcp); + if (cbcp->fsm.type == CBCP_NONUM) { + /* + * Don't change state in case the peer doesn't get our ACK, + * just bring the layer up. + */ + timer_Stop(&cbcp->fsm.timer); + datalink_NCPUp(cbcp->p->dl); + } + break; + + default: + datalink_CBCPFailed(cbcp->p->dl); + break; + } + } else + log_Printf(LogCBCP, "%s: unexpected RESPONSE dropped\n", p->dl->name); + break; + + case CBCP_ACK: + log_Printf(LogCBCP, "%s: RecvAck(%d) state = %s\n", + p->dl->name, head->id, cbcpstate(cbcp->fsm.state)); + cbcp_data_Show(data); + if (cbcp->fsm.id != head->id) { + log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n", + cbcp->fsm.id, head->id); + cbcp->fsm.id = head->id; + } + if (cbcp->fsm.type == CBCP_NONUM) { + /* + * Don't change state in case the peer doesn't get our ACK, + * just bring the layer up. + */ + timer_Stop(&cbcp->fsm.timer); + datalink_NCPUp(cbcp->p->dl); + } else if (cbcp->fsm.state == CBCP_RESPSENT) { + timer_Stop(&cbcp->fsm.timer); + datalink_CBCPComplete(cbcp->p->dl); + log_Printf(LogPHASE, "%s: CBCP: Peer will dial back\n", p->dl->name); + } else + log_Printf(LogCBCP, "%s: unexpected ACK dropped\n", p->dl->name); + break; + + default: + log_Printf(LogWARN, "Unrecognised CBCP packet (code %d, length %zd)\n", + head->code, len); + break; + } + + m_freem(bp); + return NULL; +} + +void +cbcp_Down(struct cbcp *cbcp) +{ + timer_Stop(&cbcp->fsm.timer); + cbcp_NewPhase(cbcp, CBCP_CLOSED); + cbcp->required = 0; +} + +void +cbcp_ReceiveTerminateReq(struct physical *p) +{ + if (p->dl->cbcp.fsm.state == CBCP_ACKSENT) { + /* Don't change our state in case the peer doesn't get the ACK */ + p->dl->cbcp.required = 1; + log_Printf(LogPHASE, "%s: CBCP: Will dial back on %s\n", p->dl->name, + p->dl->cbcp.fsm.phone); + } else + cbcp_NewPhase(&p->dl->cbcp, CBCP_CLOSED); +} -- cgit v1.2.3