cbcp.c revision 40655
138175Sbrian/*-
238175Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
338175Sbrian * All rights reserved.
438175Sbrian *
538175Sbrian * Redistribution and use in source and binary forms, with or without
638175Sbrian * modification, are permitted provided that the following conditions
738175Sbrian * are met:
838175Sbrian * 1. Redistributions of source code must retain the above copyright
938175Sbrian *    notice, this list of conditions and the following disclaimer.
1038175Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1138175Sbrian *    notice, this list of conditions and the following disclaimer in the
1238175Sbrian *    documentation and/or other materials provided with the distribution.
1338175Sbrian *
1438175Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538175Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638175Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738175Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838175Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938175Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038175Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138175Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238175Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338175Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438175Sbrian * SUCH DAMAGE.
2538175Sbrian *
2640655Sbrian *	$Id: cbcp.c,v 1.5 1998/10/17 12:28:11 brian Exp $
2738175Sbrian */
2838175Sbrian
2938175Sbrian#include <sys/types.h>
3038175Sbrian
3138175Sbrian#include <sys/un.h>
3238175Sbrian
3338175Sbrian#include <string.h>
3438175Sbrian#include <termios.h>
3538175Sbrian
3638175Sbrian#include "defs.h"
3738175Sbrian#include "log.h"
3838175Sbrian#include "timer.h"
3938175Sbrian#include "descriptor.h"
4038175Sbrian#include "lqr.h"
4138175Sbrian#include "mbuf.h"
4238175Sbrian#include "fsm.h"
4338175Sbrian#include "lcp.h"
4438175Sbrian#include "throughput.h"
4538175Sbrian#include "hdlc.h"
4638175Sbrian#include "ccp.h"
4738175Sbrian#include "link.h"
4838175Sbrian#include "async.h"
4938175Sbrian#include "physical.h"
5038175Sbrian#include "lcpproto.h"
5138175Sbrian#include "cbcp.h"
5238175Sbrian#include "mp.h"
5338175Sbrian#include "chat.h"
5438175Sbrian#include "auth.h"
5538175Sbrian#include "chap.h"
5638175Sbrian#include "datalink.h"
5738175Sbrian
5838175Sbrianvoid
5938175Sbriancbcp_Init(struct cbcp *cbcp, struct physical *p)
6038175Sbrian{
6138175Sbrian  cbcp->required = 0;
6238175Sbrian  cbcp->fsm.state = CBCP_CLOSED;
6338175Sbrian  cbcp->fsm.id = 0;
6438175Sbrian  cbcp->fsm.delay = 0;
6538175Sbrian  *cbcp->fsm.phone = '\0';
6638175Sbrian  memset(&cbcp->fsm.timer, '\0', sizeof cbcp->fsm.timer);
6738175Sbrian  cbcp->p = p;
6838175Sbrian}
6938175Sbrian
7038175Sbrianstatic void cbcp_SendReq(struct cbcp *);
7138175Sbrianstatic void cbcp_SendResponse(struct cbcp *);
7238175Sbrianstatic void cbcp_SendAck(struct cbcp *);
7338175Sbrian
7438175Sbrianstatic void
7538175Sbriancbcp_Timeout(void *v)
7638175Sbrian{
7738175Sbrian  struct cbcp *cbcp = (struct cbcp *)v;
7838175Sbrian
7938175Sbrian  timer_Stop(&cbcp->fsm.timer);
8038175Sbrian  if (cbcp->fsm.restart) {
8138175Sbrian    switch (cbcp->fsm.state) {
8238175Sbrian      case CBCP_CLOSED:
8338175Sbrian      case CBCP_STOPPED:
8438175Sbrian        log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
8538175Sbrian                   cbcp->p->dl->name);
8638175Sbrian        break;
8738175Sbrian
8838175Sbrian      case CBCP_REQSENT:
8938175Sbrian        cbcp_SendReq(cbcp);
9038175Sbrian        break;
9138175Sbrian      case CBCP_RESPSENT:
9238175Sbrian        cbcp_SendResponse(cbcp);
9338175Sbrian        break;
9438175Sbrian      case CBCP_ACKSENT:
9538175Sbrian        cbcp_SendAck(cbcp);
9638175Sbrian        break;
9738175Sbrian    }
9838175Sbrian  } else {
9938175Sbrian    const char *missed;
10038175Sbrian
10138175Sbrian    switch (cbcp->fsm.state) {
10238175Sbrian      case CBCP_STOPPED:
10338175Sbrian        missed = "REQ";
10438175Sbrian        break;
10538175Sbrian      case CBCP_REQSENT:
10638175Sbrian        missed = "RESPONSE";
10738175Sbrian        break;
10838175Sbrian      case CBCP_RESPSENT:
10938175Sbrian        missed = "ACK";
11038175Sbrian        break;
11138175Sbrian      case CBCP_ACKSENT:
11238175Sbrian        missed = "Terminate REQ";
11338175Sbrian        break;
11438175Sbrian      default:
11538175Sbrian        log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
11638175Sbrian                   cbcp->p->dl->name);
11738175Sbrian        missed = NULL;
11838175Sbrian        break;
11938175Sbrian    }
12038175Sbrian    if (missed)
12138175Sbrian      log_Printf(LogCBCP, "%s: Timeout waiting for peer %s\n",
12238175Sbrian                 cbcp->p->dl->name, missed);
12338175Sbrian    datalink_CBCPFailed(cbcp->p->dl);
12438175Sbrian  }
12538175Sbrian}
12638175Sbrian
12738175Sbrianstatic void
12838175Sbriancbcp_StartTimer(struct cbcp *cbcp, int timeout)
12938175Sbrian{
13038175Sbrian  timer_Stop(&cbcp->fsm.timer);
13138175Sbrian  cbcp->fsm.timer.func = cbcp_Timeout;
13238175Sbrian  cbcp->fsm.timer.name = "cbcp";
13338175Sbrian  cbcp->fsm.timer.load = timeout * SECTICKS;
13438175Sbrian  cbcp->fsm.timer.arg = cbcp;
13538175Sbrian  timer_Start(&cbcp->fsm.timer);
13638175Sbrian}
13738175Sbrian
13838175Sbrian#define CBCP_CLOSED	(0)	/* Not in use */
13938175Sbrian#define CBCP_STOPPED	(1)	/* Waiting for a REQ */
14038175Sbrian#define CBCP_REQSENT	(2)	/* Waiting for a RESP */
14138175Sbrian#define CBCP_RESPSENT	(3)	/* Waiting for an ACK */
14238175Sbrian#define CBCP_ACKSENT	(4)	/* Waiting for an LCP Term REQ */
14338175Sbrian
14438175Sbrianstatic const char *cbcpname[] = {
14538175Sbrian  "closed", "stopped", "req-sent", "resp-sent", "ack-sent"
14638175Sbrian};
14738175Sbrian
14838175Sbrianstatic const char *
14938175Sbriancbcpstate(int s)
15038175Sbrian{
15138175Sbrian  if (s < sizeof cbcpname / sizeof cbcpname[0])
15238175Sbrian    return cbcpname[s];
15338175Sbrian  return "???";
15438175Sbrian}
15538175Sbrian
15638175Sbrianstatic void
15738175Sbriancbcp_NewPhase(struct cbcp *cbcp, int new)
15838175Sbrian{
15938175Sbrian  if (cbcp->fsm.state != new) {
16038175Sbrian    log_Printf(LogCBCP, "%s: State change %s --> %s\n", cbcp->p->dl->name,
16138175Sbrian               cbcpstate(cbcp->fsm.state), cbcpstate(new));
16238175Sbrian    cbcp->fsm.state = new;
16338175Sbrian  }
16438175Sbrian}
16538175Sbrian
16638175Sbrianstruct cbcp_header {
16738175Sbrian  u_char code;
16838175Sbrian  u_char id;
16938175Sbrian  u_int16_t length;	/* Network byte order */
17038175Sbrian};
17138175Sbrian
17238175Sbrian
17338175Sbrian/* cbcp_header::code values */
17438175Sbrian#define CBCP_REQ	(1)
17538175Sbrian#define CBCP_RESPONSE	(2)
17638175Sbrian#define CBCP_ACK	(3)
17738175Sbrian
17838175Sbrianstruct cbcp_data {
17938175Sbrian  u_char type;
18038175Sbrian  u_char length;
18138175Sbrian  u_char delay;
18238175Sbrian  char addr_start[253];	/* max cbcp_data length 255 + 1 for NULL */
18338175Sbrian};
18438175Sbrian
18538175Sbrian/* cbcp_data::type values */
18638175Sbrian#define CBCP_NONUM	(1)
18738175Sbrian#define CBCP_CLIENTNUM	(2)
18838175Sbrian#define CBCP_SERVERNUM	(3)
18938175Sbrian#define CBCP_LISTNUM	(4)
19038175Sbrian
19138175Sbrianstatic void
19238175Sbriancbcp_Output(struct cbcp *cbcp, u_char code, struct cbcp_data *data)
19338175Sbrian{
19438175Sbrian  struct cbcp_header *head;
19538175Sbrian  struct mbuf *bp;
19638175Sbrian
19738175Sbrian  bp = mbuf_Alloc(sizeof *head + data->length, MB_CBCP);
19838175Sbrian  head = (struct cbcp_header *)MBUF_CTOP(bp);
19938175Sbrian  head->code = code;
20038175Sbrian  head->id = cbcp->fsm.id;
20138175Sbrian  head->length = htons(sizeof *head + data->length);
20238175Sbrian  memcpy(MBUF_CTOP(bp) + sizeof *head, data, data->length);
20338175Sbrian  log_DumpBp(LogDEBUG, "cbcp_Output", bp);
20438175Sbrian  hdlc_Output(&cbcp->p->link, PRI_LINK, PROTO_CBCP, bp);
20538175Sbrian}
20638175Sbrian
20738175Sbrianstatic const char *
20838175Sbriancbcp_data_Type(int type)
20938175Sbrian{
21038175Sbrian  static const char *types[] = {
21138175Sbrian    "No callback", "User-spec", "Server-spec", "list"
21238175Sbrian  };
21338175Sbrian
21438175Sbrian  if (type < 1 || type > sizeof types / sizeof types[0])
21538175Sbrian    return "???";
21638175Sbrian  return types[type-1];
21738175Sbrian}
21838175Sbrian
21938175Sbrianstruct cbcp_addr {
22038175Sbrian  u_char type;
22138175Sbrian  char addr[1];		/* Really ASCIIZ */
22238175Sbrian};
22338175Sbrian
22438175Sbrian/* cbcp_data::type values */
22538175Sbrian#define CBCP_ADDR_PSTN	(1)
22638175Sbrian
22738175Sbrianstatic void
22838175Sbriancbcp_data_Show(struct cbcp_data *data)
22938175Sbrian{
23038175Sbrian  struct cbcp_addr *addr;
23138175Sbrian  char *end;
23238175Sbrian
23338175Sbrian  addr = (struct cbcp_addr *)data->addr_start;
23438175Sbrian  end = (char *)data + data->length;
23538175Sbrian  *end = '\0';
23638175Sbrian
23738175Sbrian  log_Printf(LogCBCP, " TYPE %s\n", cbcp_data_Type(data->type));
23838175Sbrian  if ((char *)&data->delay < end) {
23938175Sbrian    log_Printf(LogCBCP, " DELAY %d\n", data->delay);
24038175Sbrian    while (addr->addr < end) {
24138175Sbrian      if (addr->type == CBCP_ADDR_PSTN)
24238175Sbrian        log_Printf(LogCBCP, " ADDR %s\n", addr->addr);
24338175Sbrian      else
24438175Sbrian        log_Printf(LogCBCP, " ADDR type %d ??\n", (int)addr->type);
24538175Sbrian      addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
24638175Sbrian    }
24738175Sbrian  }
24838175Sbrian}
24938175Sbrian
25038175Sbrianstatic void
25138175Sbriancbcp_SendReq(struct cbcp *cbcp)
25238175Sbrian{
25338175Sbrian  struct cbcp_data data;
25438175Sbrian  struct cbcp_addr *addr;
25538175Sbrian  char list[sizeof cbcp->fsm.phone], *next;
25638175Sbrian  int len, max;
25738175Sbrian
25838175Sbrian  /* Only callees send REQs */
25938175Sbrian
26038175Sbrian  log_Printf(LogCBCP, "%s: SendReq(%d) state = %s\n", cbcp->p->dl->name,
26138175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
26238175Sbrian  data.type = cbcp->fsm.type;
26338175Sbrian  data.delay = 0;
26438175Sbrian  strncpy(list, cbcp->fsm.phone, sizeof list - 1);
26538175Sbrian  list[sizeof list - 1] = '\0';
26638175Sbrian
26738175Sbrian  switch (data.type) {
26838175Sbrian    case CBCP_CLIENTNUM:
26938175Sbrian      addr = (struct cbcp_addr *)data.addr_start;
27038175Sbrian      addr->type = CBCP_ADDR_PSTN;
27138175Sbrian      *addr->addr = '\0';
27238175Sbrian      data.length = addr->addr - (char *)&data;
27338175Sbrian      break;
27438175Sbrian
27538175Sbrian    case CBCP_LISTNUM:
27638175Sbrian      addr = (struct cbcp_addr *)data.addr_start;
27738175Sbrian      for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
27838175Sbrian        len = strlen(next);
27938175Sbrian        max = data.addr_start + sizeof data.addr_start - addr->addr - 1;
28038175Sbrian        if (len <= max) {
28138175Sbrian          addr->type = CBCP_ADDR_PSTN;
28238175Sbrian          strcpy(addr->addr, next);
28338175Sbrian          addr = (struct cbcp_addr *)((char *)addr + len + 2);
28438175Sbrian        } else
28538175Sbrian          log_Printf(LogWARN, "CBCP ADDR \"%s\" skipped - packet too large\n",
28638175Sbrian                     next);
28738175Sbrian      }
28838175Sbrian      data.length = (char *)addr - (char *)&data;
28938175Sbrian      break;
29038175Sbrian
29138175Sbrian    case CBCP_SERVERNUM:
29238175Sbrian      data.length = data.addr_start - (char *)&data;
29338175Sbrian      break;
29438175Sbrian
29538175Sbrian    default:
29640655Sbrian      data.length = (char *)&data.delay - (char *)&data;
29738175Sbrian      break;
29838175Sbrian  }
29938175Sbrian
30038175Sbrian  cbcp_data_Show(&data);
30138175Sbrian  cbcp_Output(cbcp, CBCP_REQ, &data);
30238175Sbrian  cbcp->fsm.restart--;
30338175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
30438175Sbrian  cbcp_NewPhase(cbcp, CBCP_REQSENT);		/* Wait for a RESPONSE */
30538175Sbrian}
30638175Sbrian
30738175Sbrianvoid
30838175Sbriancbcp_Up(struct cbcp *cbcp)
30938175Sbrian{
31038175Sbrian  struct lcp *lcp = &cbcp->p->link.lcp;
31138175Sbrian
31238175Sbrian  cbcp->fsm.delay = cbcp->p->dl->cfg.cbcp.delay;
31338175Sbrian  if (*cbcp->p->dl->peer.authname == '\0' ||
31438175Sbrian      !auth_SetPhoneList(cbcp->p->dl->peer.authname, cbcp->fsm.phone,
31538175Sbrian                         sizeof cbcp->fsm.phone)) {
31638175Sbrian    strncpy(cbcp->fsm.phone, cbcp->p->dl->cfg.cbcp.phone,
31738175Sbrian            sizeof cbcp->fsm.phone - 1);
31838175Sbrian    cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
31938175Sbrian  }
32038175Sbrian
32138175Sbrian  if (lcp->want_callback.opmask) {
32238175Sbrian    if (*cbcp->fsm.phone == '\0')
32338175Sbrian      cbcp->fsm.type = CBCP_NONUM;
32438175Sbrian    else if (!strcmp(cbcp->fsm.phone, "*")) {
32538175Sbrian      cbcp->fsm.type = CBCP_SERVERNUM;
32638175Sbrian      *cbcp->fsm.phone = '\0';
32738175Sbrian    } else
32838175Sbrian      cbcp->fsm.type = CBCP_CLIENTNUM;
32938175Sbrian    cbcp_NewPhase(cbcp, CBCP_STOPPED);		/* Wait for a REQ */
33038175Sbrian    cbcp_StartTimer(cbcp, cbcp->fsm.delay * DEF_REQs);
33138175Sbrian  } else {
33238175Sbrian    if (*cbcp->fsm.phone == '\0')
33338175Sbrian      cbcp->fsm.type = CBCP_NONUM;
33438175Sbrian    else if (!strcmp(cbcp->fsm.phone, "*")) {
33538175Sbrian      cbcp->fsm.type = CBCP_CLIENTNUM;
33638175Sbrian      *cbcp->fsm.phone = '\0';
33738175Sbrian    } else if (strchr(cbcp->fsm.phone, ','))
33838175Sbrian      cbcp->fsm.type = CBCP_LISTNUM;
33938175Sbrian    else
34038175Sbrian      cbcp->fsm.type = CBCP_SERVERNUM;
34138175Sbrian    cbcp->fsm.restart = DEF_REQs;
34238175Sbrian    cbcp_SendReq(cbcp);
34338175Sbrian  }
34438175Sbrian}
34538175Sbrian
34638175Sbrianstatic int
34738175Sbriancbcp_AdjustResponse(struct cbcp *cbcp, struct cbcp_data *data)
34838175Sbrian{
34938175Sbrian  /*
35038175Sbrian   * We've received a REQ (data).  Adjust our reponse (cbcp->fsm.*)
35138175Sbrian   * so that we (hopefully) agree with the peer
35238175Sbrian   */
35338175Sbrian  struct cbcp_addr *addr;
35438175Sbrian
35538175Sbrian  switch (data->type) {
35638175Sbrian    case CBCP_NONUM:
35738175Sbrian      if (cbcp->fsm.type == CBCP_NONUM)
35838175Sbrian          return 1;
35938175Sbrian      log_Printf(LogPHASE, "CBCP: server wants no callback !\n");
36038175Sbrian      return 0;
36138175Sbrian
36238175Sbrian    case CBCP_CLIENTNUM:
36338175Sbrian      if (cbcp->fsm.type == CBCP_CLIENTNUM) {
36438175Sbrian        char *ptr;
36538175Sbrian
36638175Sbrian        if (data->length > data->addr_start - (char *)data) {
36738175Sbrian          /*
36838175Sbrian           * The peer has given us an address type spec - make sure we
36938175Sbrian           * understand !
37038175Sbrian           */
37138175Sbrian          addr = (struct cbcp_addr *)data->addr_start;
37238175Sbrian          if (addr->type != CBCP_ADDR_PSTN) {
37338175Sbrian            log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
37438175Sbrian                       (int)addr->type);
37538175Sbrian            return 0;
37638175Sbrian          }
37738175Sbrian        }
37838175Sbrian        /* we accept the REQ even if the peer didn't specify an addr->type */
37938175Sbrian        ptr = strchr(cbcp->fsm.phone, ',');
38038175Sbrian        if (ptr)
38138175Sbrian          *ptr = '\0';		/* Just use the first number in our list */
38238175Sbrian        return 1;
38338175Sbrian      }
38438175Sbrian      log_Printf(LogPHASE, "CBCP: no number to pass to the peer !\n");
38538175Sbrian      return 0;
38638175Sbrian
38738175Sbrian    case CBCP_SERVERNUM:
38838175Sbrian      if (cbcp->fsm.type == CBCP_SERVERNUM) {
38938175Sbrian        *cbcp->fsm.phone = '\0';
39038175Sbrian        return 1;
39138175Sbrian      }
39238175Sbrian      if (data->length > data->addr_start - (char *)data) {
39338175Sbrian        /*
39438175Sbrian         * This violates the spec, but if the peer has told us the
39538175Sbrian         * number it wants to call back, take advantage of this fact
39638175Sbrian         * and allow things to proceed if we've specified the same
39738175Sbrian         * number
39838175Sbrian         */
39938175Sbrian        addr = (struct cbcp_addr *)data->addr_start;
40038175Sbrian        if (addr->type != CBCP_ADDR_PSTN) {
40138175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
40238175Sbrian                     (int)addr->type);
40338175Sbrian          return 0;
40438175Sbrian        } else if (cbcp->fsm.type == CBCP_CLIENTNUM) {
40538175Sbrian          /*
40638175Sbrian           * If the peer's insisting on deciding the number, make sure
40738175Sbrian           * it's one of the ones in our list.  If it is, let the peer
40838175Sbrian           * think it's in control :-)
40938175Sbrian           */
41038175Sbrian          char list[sizeof cbcp->fsm.phone], *next;
41138175Sbrian
41238175Sbrian          strncpy(list, cbcp->fsm.phone, sizeof list - 1);
41338175Sbrian          list[sizeof list - 1] = '\0';
41438175Sbrian          for (next = strtok(list, ","); next; next = strtok(NULL, ","))
41538175Sbrian            if (!strcmp(next, addr->addr)) {
41638175Sbrian              cbcp->fsm.type = CBCP_SERVERNUM;
41738175Sbrian              strcpy(cbcp->fsm.phone, next);
41838175Sbrian              return 1;
41938175Sbrian            }
42038175Sbrian        }
42138175Sbrian      }
42238175Sbrian      log_Printf(LogPHASE, "CBCP: Peer won't allow local decision !\n");
42338175Sbrian      return 0;
42438175Sbrian
42538175Sbrian    case CBCP_LISTNUM:
42638175Sbrian      if (cbcp->fsm.type == CBCP_CLIENTNUM || cbcp->fsm.type == CBCP_LISTNUM) {
42738175Sbrian        /*
42838175Sbrian         * Search through ``data''s addresses and see if cbcp->fsm.phone
42938175Sbrian         * contains any of them
43038175Sbrian         */
43138175Sbrian        char list[sizeof cbcp->fsm.phone], *next, *end;
43238175Sbrian
43338175Sbrian        addr = (struct cbcp_addr *)data->addr_start;
43438175Sbrian        end = (char *)data + data->length;
43538175Sbrian
43638175Sbrian        while (addr->addr < end) {
43738175Sbrian          if (addr->type == CBCP_ADDR_PSTN) {
43838175Sbrian            strncpy(list, cbcp->fsm.phone, sizeof list - 1);
43938175Sbrian            list[sizeof list - 1] = '\0';
44038175Sbrian            for (next = strtok(list, ","); next; next = strtok(NULL, ","))
44138175Sbrian              if (!strcmp(next, addr->addr)) {
44238175Sbrian                cbcp->fsm.type = CBCP_LISTNUM;
44338175Sbrian                strcpy(cbcp->fsm.phone, next);
44438175Sbrian                return 1;
44538175Sbrian              }
44638175Sbrian          } else
44738175Sbrian            log_Printf(LogCBCP, "Warning: Unrecognised address type %d !\n",
44838175Sbrian                       (int)addr->type);
44938175Sbrian          addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
45038175Sbrian        }
45138175Sbrian      }
45238175Sbrian      log_Printf(LogPHASE, "CBCP: no good number to pass to the peer !\n");
45338175Sbrian      return 0;
45438175Sbrian  }
45538175Sbrian
45638175Sbrian  log_Printf(LogCBCP, "Unrecognised REQ type %d !\n", (int)data->type);
45738175Sbrian  return 0;
45838175Sbrian}
45938175Sbrian
46038175Sbrianstatic void
46138175Sbriancbcp_SendResponse(struct cbcp *cbcp)
46238175Sbrian{
46338175Sbrian  struct cbcp_data data;
46438175Sbrian  struct cbcp_addr *addr;
46538175Sbrian
46638175Sbrian  /* Only callers send RESPONSEs */
46738175Sbrian
46838175Sbrian  log_Printf(LogCBCP, "%s: SendResponse(%d) state = %s\n", cbcp->p->dl->name,
46938175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
47038175Sbrian
47138175Sbrian  data.type = cbcp->fsm.type;
47238175Sbrian  data.delay = cbcp->fsm.delay;
47338175Sbrian  addr = (struct cbcp_addr *)data.addr_start;
47440655Sbrian  if (data.type == CBCP_NONUM)
47540655Sbrian    data.length = (char *)&data.delay - (char *)&data;
47640655Sbrian  else if (*cbcp->fsm.phone) {
47738175Sbrian    addr->type = CBCP_ADDR_PSTN;
47838175Sbrian    strcpy(addr->addr, cbcp->fsm.phone);
47938175Sbrian    data.length = (addr->addr + strlen(addr->addr) + 1) - (char *)&data;
48038175Sbrian  } else
48138175Sbrian    data.length = data.addr_start - (char *)&data;
48238175Sbrian
48338175Sbrian  cbcp_data_Show(&data);
48438175Sbrian  cbcp_Output(cbcp, CBCP_RESPONSE, &data);
48538175Sbrian  cbcp->fsm.restart--;
48638175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
48738175Sbrian  cbcp_NewPhase(cbcp, CBCP_RESPSENT);	/* Wait for an ACK */
48838175Sbrian}
48938175Sbrian
49038175Sbrian/* What to do after checking an incoming response */
49138175Sbrian#define CBCP_ACTION_DOWN (0)
49238175Sbrian#define CBCP_ACTION_REQ (1)
49338175Sbrian#define CBCP_ACTION_ACK (2)
49438175Sbrian
49538175Sbrianstatic int
49638175Sbriancbcp_CheckResponse(struct cbcp *cbcp, struct cbcp_data *data)
49738175Sbrian{
49838175Sbrian  /*
49938175Sbrian   * We've received a RESPONSE (data).  Check if it agrees with
50038175Sbrian   * our REQ (cbcp->fsm)
50138175Sbrian   */
50238175Sbrian  struct cbcp_addr *addr;
50338175Sbrian
50438175Sbrian  addr = (struct cbcp_addr *)data->addr_start;
50538175Sbrian
50638175Sbrian  if (data->type == cbcp->fsm.type) {
50738175Sbrian    switch (cbcp->fsm.type) {
50838175Sbrian      case CBCP_NONUM:
50938175Sbrian        return CBCP_ACTION_ACK;
51038175Sbrian
51138175Sbrian      case CBCP_CLIENTNUM:
51238175Sbrian        if ((char *)data + data->length <= addr->addr)
51338175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
51438175Sbrian        else if (addr->type != CBCP_ADDR_PSTN)
51538175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
51638175Sbrian                     addr->type);
51738175Sbrian        else {
51838175Sbrian          strcpy(cbcp->fsm.phone, addr->addr);
51938175Sbrian          cbcp->fsm.delay = data->delay;
52038175Sbrian          return CBCP_ACTION_ACK;
52138175Sbrian        }
52238175Sbrian        return CBCP_ACTION_DOWN;
52338175Sbrian
52438175Sbrian      case CBCP_SERVERNUM:
52538175Sbrian        cbcp->fsm.delay = data->delay;
52638175Sbrian        return CBCP_ACTION_ACK;
52738175Sbrian
52838175Sbrian      case CBCP_LISTNUM:
52938175Sbrian        if ((char *)data + data->length <= addr->addr)
53038175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
53138175Sbrian        else if (addr->type != CBCP_ADDR_PSTN)
53238175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
53338175Sbrian                     addr->type);
53438175Sbrian        else {
53538175Sbrian          char list[sizeof cbcp->fsm.phone], *next;
53638175Sbrian
53738175Sbrian          strncpy(list, cbcp->fsm.phone, sizeof list - 1);
53838175Sbrian          list[sizeof list - 1] = '\0';
53938175Sbrian          for (next = strtok(list, ","); next; next = strtok(NULL, ","))
54038175Sbrian            if (!strcmp(addr->addr, next)) {
54138175Sbrian              strcpy(cbcp->fsm.phone, next);
54238175Sbrian              cbcp->fsm.delay = data->delay;
54338175Sbrian              return CBCP_ACTION_ACK;
54438175Sbrian            }
54538175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a "
54638175Sbrian                     "valid number !\n");
54738175Sbrian        }
54838175Sbrian        return CBCP_ACTION_DOWN;
54938175Sbrian    }
55038175Sbrian    log_Printf(LogPHASE, "Internal CBCP error - agreed on %d ??!?\n",
55138175Sbrian               (int)cbcp->fsm.type);
55238175Sbrian    return CBCP_ACTION_DOWN;
55340484Sbrian  } else if (data->type == CBCP_NONUM && cbcp->fsm.type == CBCP_CLIENTNUM) {
55440484Sbrian    /*
55540484Sbrian     * Client doesn't want CBCP after all....
55640484Sbrian     * We only allow this when ``set cbcp *'' has been specified.
55740484Sbrian     */
55840484Sbrian    cbcp->fsm.type = CBCP_NONUM;
55940484Sbrian    return CBCP_ACTION_ACK;
56038175Sbrian  }
56138175Sbrian  log_Printf(LogCBCP, "Invalid peer RESPONSE\n");
56238175Sbrian  return CBCP_ACTION_REQ;
56338175Sbrian}
56438175Sbrian
56538175Sbrianstatic void
56638175Sbriancbcp_SendAck(struct cbcp *cbcp)
56738175Sbrian{
56838175Sbrian  struct cbcp_data data;
56940655Sbrian  char *end;
57038175Sbrian
57138175Sbrian  /* Only callees send ACKs */
57238175Sbrian
57338175Sbrian  log_Printf(LogCBCP, "%s: SendAck(%d) state = %s\n", cbcp->p->dl->name,
57438175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
57538175Sbrian
57638175Sbrian  data.type = cbcp->fsm.type;
57738175Sbrian  data.delay = cbcp->fsm.delay;
57840655Sbrian  end = data.type == CBCP_NONUM ? (char *)&data.delay : data.addr_start;
57940655Sbrian  data.length = end - (char *)&data;
58038175Sbrian
58138175Sbrian  cbcp_data_Show(&data);
58238175Sbrian  cbcp_Output(cbcp, CBCP_ACK, &data);
58338175Sbrian  cbcp->fsm.restart--;
58438175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
58538175Sbrian  cbcp_NewPhase(cbcp, CBCP_ACKSENT);	/* Wait for an ACK */
58638175Sbrian}
58738175Sbrian
58838175Sbrianvoid
58938175Sbriancbcp_Input(struct physical *p, struct mbuf *bp)
59038175Sbrian{
59138175Sbrian  struct cbcp_header *head;
59238175Sbrian  struct cbcp_data *data;
59338175Sbrian  struct cbcp *cbcp = &p->dl->cbcp;
59438175Sbrian  int len;
59538175Sbrian
59638175Sbrian  len = mbuf_Length(bp);
59738175Sbrian  if (len < sizeof(struct cbcp_header)) {
59838175Sbrian    mbuf_Free(bp);
59938175Sbrian    return;
60038175Sbrian  }
60138175Sbrian  head = (struct cbcp_header *)MBUF_CTOP(bp);
60238175Sbrian  if (ntohs(head->length) != len) {
60338175Sbrian    log_Printf(LogWARN, "Corrupt CBCP packet (code %d, length %d not %d)"
60438175Sbrian               " - ignored\n", head->code, ntohs(head->length), len);
60538175Sbrian    mbuf_Free(bp);
60638175Sbrian    return;
60738175Sbrian  }
60838175Sbrian
60938175Sbrian  /* XXX check the id */
61038175Sbrian
61138175Sbrian  bp->offset += sizeof(struct cbcp_header);
61238175Sbrian  bp->cnt -= sizeof(struct cbcp_header);
61338175Sbrian  data = (struct cbcp_data *)MBUF_CTOP(bp);
61438175Sbrian
61538175Sbrian  switch (head->code) {
61638175Sbrian    case CBCP_REQ:
61738175Sbrian      log_Printf(LogCBCP, "%s: RecvReq(%d) state = %s\n",
61838175Sbrian                 p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
61938175Sbrian      cbcp_data_Show(data);
62038175Sbrian      if (cbcp->fsm.state == CBCP_STOPPED || cbcp->fsm.state == CBCP_RESPSENT) {
62138175Sbrian        timer_Stop(&cbcp->fsm.timer);
62238175Sbrian        if (cbcp_AdjustResponse(cbcp, data)) {
62338175Sbrian          cbcp->fsm.restart = DEF_REQs;
62440485Sbrian          cbcp->fsm.id = head->id;
62538175Sbrian          cbcp_SendResponse(cbcp);
62638175Sbrian        } else
62738175Sbrian          datalink_CBCPFailed(cbcp->p->dl);
62838175Sbrian      } else
62938175Sbrian        log_Printf(LogCBCP, "%s: unexpected REQ dropped\n", p->dl->name);
63038175Sbrian      break;
63138175Sbrian
63238175Sbrian    case CBCP_RESPONSE:
63338175Sbrian      log_Printf(LogCBCP, "%s: RecvResponse(%d) state = %s\n",
63438175Sbrian	         p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
63538175Sbrian      cbcp_data_Show(data);
63640486Sbrian      if (cbcp->fsm.id != head->id) {
63740486Sbrian        log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
63840486Sbrian                   cbcp->fsm.id, head->id);
63940486Sbrian        cbcp->fsm.id = head->id;
64040486Sbrian      }
64138175Sbrian      if (cbcp->fsm.state == CBCP_REQSENT || cbcp->fsm.state == CBCP_ACKSENT) {
64238175Sbrian        timer_Stop(&cbcp->fsm.timer);
64338175Sbrian        switch (cbcp_CheckResponse(cbcp, data)) {
64438175Sbrian          case CBCP_ACTION_REQ:
64538175Sbrian            cbcp_SendReq(cbcp);
64638175Sbrian            break;
64738175Sbrian
64838175Sbrian          case CBCP_ACTION_ACK:
64938175Sbrian            cbcp->fsm.restart = DEF_REQs;
65038175Sbrian            cbcp_SendAck(cbcp);
65138175Sbrian            if (cbcp->fsm.type == CBCP_NONUM) {
65238175Sbrian              /*
65338175Sbrian               * Don't change state in case the peer doesn't get our ACK,
65438175Sbrian               * just bring the layer up.
65538175Sbrian               */
65638175Sbrian              timer_Stop(&cbcp->fsm.timer);
65738175Sbrian              datalink_NCPUp(cbcp->p->dl);
65838175Sbrian            }
65938175Sbrian            break;
66038175Sbrian
66138175Sbrian          default:
66238175Sbrian            datalink_CBCPFailed(cbcp->p->dl);
66338175Sbrian            break;
66438175Sbrian        }
66538175Sbrian      } else
66638175Sbrian        log_Printf(LogCBCP, "%s: unexpected RESPONSE dropped\n", p->dl->name);
66738175Sbrian      break;
66838175Sbrian
66938175Sbrian    case CBCP_ACK:
67038175Sbrian      log_Printf(LogCBCP, "%s: RecvAck(%d) state = %s\n",
67138175Sbrian	         p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
67238175Sbrian      cbcp_data_Show(data);
67340486Sbrian      if (cbcp->fsm.id != head->id) {
67440486Sbrian        log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
67540486Sbrian                   cbcp->fsm.id, head->id);
67640486Sbrian        cbcp->fsm.id = head->id;
67740486Sbrian      }
67838175Sbrian      if (cbcp->fsm.state == CBCP_RESPSENT) {
67938175Sbrian        timer_Stop(&cbcp->fsm.timer);
68038175Sbrian        datalink_CBCPComplete(cbcp->p->dl);
68138175Sbrian        log_Printf(LogPHASE, "%s: CBCP: Peer will dial back\n", p->dl->name);
68238175Sbrian      } else
68338175Sbrian        log_Printf(LogCBCP, "%s: unexpected ACK dropped\n", p->dl->name);
68438175Sbrian      break;
68538175Sbrian
68638175Sbrian    default:
68738175Sbrian      log_Printf(LogWARN, "Unrecognised CBCP packet (code %d, length %d)\n",
68838175Sbrian               head->code, len);
68938175Sbrian      break;
69038175Sbrian  }
69138175Sbrian
69238175Sbrian  mbuf_Free(bp);
69338175Sbrian}
69438175Sbrian
69538175Sbrianvoid
69638175Sbriancbcp_Down(struct cbcp *cbcp)
69738175Sbrian{
69838175Sbrian  timer_Stop(&cbcp->fsm.timer);
69938175Sbrian  cbcp_NewPhase(cbcp, CBCP_CLOSED);
70038175Sbrian  cbcp->required = 0;
70138175Sbrian}
70238175Sbrian
70338175Sbrianvoid
70438175Sbriancbcp_ReceiveTerminateReq(struct physical *p)
70538175Sbrian{
70638175Sbrian  if (p->dl->cbcp.fsm.state == CBCP_ACKSENT) {
70738175Sbrian    /* Don't change our state in case the peer doesn't get the ACK */
70838175Sbrian    p->dl->cbcp.required = 1;
70938175Sbrian    log_Printf(LogPHASE, "%s: CBCP: Will dial back on %s\n", p->dl->name,
71038175Sbrian               p->dl->cbcp.fsm.phone);
71138175Sbrian  } else
71238175Sbrian    cbcp_NewPhase(&p->dl->cbcp, CBCP_CLOSED);
71338175Sbrian}
714