The SPDX folks have obsoleted the BSD-2-Clause-FreeBSD identifier. Catch up to that fact and revert to their recommended match of BSD-2-Clause. Discussed with: pfg MFC After: 3 days Sponsored by: Netflix
766 lines
22 KiB
C
766 lines
22 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* 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$
|
|
*/
|
|
|
|
#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 response (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);
|
|
}
|