| /*- |
| * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> |
| * 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 <sys/param.h> |
| |
| #ifdef __FreeBSD__ |
| #include <netinet/in.h> |
| #endif |
| #include <sys/un.h> |
| |
| #include <string.h> |
| #include <termios.h> |
| |
| #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); |
| } |