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 *
2650479Speter * $FreeBSD$
2738175Sbrian */
2838175Sbrian
2943313Sbrian#include <sys/param.h>
3038175Sbrian
3196582Sbrian#ifdef __FreeBSD__
3296582Sbrian#include <netinet/in.h>
3396582Sbrian#endif
3438175Sbrian#include <sys/un.h>
3538175Sbrian
3638175Sbrian#include <string.h>
3738175Sbrian#include <termios.h>
3838175Sbrian
3946686Sbrian#include "layer.h"
4038175Sbrian#include "defs.h"
4138175Sbrian#include "log.h"
4238175Sbrian#include "timer.h"
4338175Sbrian#include "descriptor.h"
4438175Sbrian#include "lqr.h"
4538175Sbrian#include "mbuf.h"
4638175Sbrian#include "fsm.h"
4738175Sbrian#include "throughput.h"
4838175Sbrian#include "hdlc.h"
4963484Sbrian#include "lcp.h"
5038175Sbrian#include "ccp.h"
5138175Sbrian#include "link.h"
5238175Sbrian#include "async.h"
5338175Sbrian#include "physical.h"
5446686Sbrian#include "proto.h"
5538175Sbrian#include "cbcp.h"
5638175Sbrian#include "mp.h"
5738175Sbrian#include "chat.h"
5838175Sbrian#include "auth.h"
5938175Sbrian#include "chap.h"
6038175Sbrian#include "datalink.h"
6138175Sbrian
6238175Sbrianvoid
6338175Sbriancbcp_Init(struct cbcp *cbcp, struct physical *p)
6438175Sbrian{
6538175Sbrian  cbcp->required = 0;
6638175Sbrian  cbcp->fsm.state = CBCP_CLOSED;
6738175Sbrian  cbcp->fsm.id = 0;
6838175Sbrian  cbcp->fsm.delay = 0;
6938175Sbrian  *cbcp->fsm.phone = '\0';
7038175Sbrian  memset(&cbcp->fsm.timer, '\0', sizeof cbcp->fsm.timer);
7138175Sbrian  cbcp->p = p;
7238175Sbrian}
7338175Sbrian
7438175Sbrianstatic void cbcp_SendReq(struct cbcp *);
7538175Sbrianstatic void cbcp_SendResponse(struct cbcp *);
7638175Sbrianstatic void cbcp_SendAck(struct cbcp *);
7738175Sbrian
7898243Sbrianstatic void
7938175Sbriancbcp_Timeout(void *v)
8038175Sbrian{
8138175Sbrian  struct cbcp *cbcp = (struct cbcp *)v;
8238175Sbrian
8338175Sbrian  timer_Stop(&cbcp->fsm.timer);
8438175Sbrian  if (cbcp->fsm.restart) {
8538175Sbrian    switch (cbcp->fsm.state) {
8638175Sbrian      case CBCP_CLOSED:
8738175Sbrian      case CBCP_STOPPED:
8838175Sbrian        log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
8938175Sbrian                   cbcp->p->dl->name);
9038175Sbrian        break;
9138175Sbrian
9238175Sbrian      case CBCP_REQSENT:
9338175Sbrian        cbcp_SendReq(cbcp);
9438175Sbrian        break;
9538175Sbrian      case CBCP_RESPSENT:
9638175Sbrian        cbcp_SendResponse(cbcp);
9738175Sbrian        break;
9838175Sbrian      case CBCP_ACKSENT:
9938175Sbrian        cbcp_SendAck(cbcp);
10038175Sbrian        break;
10138175Sbrian    }
10238175Sbrian  } else {
10338175Sbrian    const char *missed;
10438175Sbrian
10538175Sbrian    switch (cbcp->fsm.state) {
10638175Sbrian      case CBCP_STOPPED:
10738175Sbrian        missed = "REQ";
10838175Sbrian        break;
10938175Sbrian      case CBCP_REQSENT:
11038175Sbrian        missed = "RESPONSE";
11138175Sbrian        break;
11238175Sbrian      case CBCP_RESPSENT:
11338175Sbrian        missed = "ACK";
11438175Sbrian        break;
11538175Sbrian      case CBCP_ACKSENT:
11638175Sbrian        missed = "Terminate REQ";
11738175Sbrian        break;
11838175Sbrian      default:
11938175Sbrian        log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
12038175Sbrian                   cbcp->p->dl->name);
12138175Sbrian        missed = NULL;
12238175Sbrian        break;
12338175Sbrian    }
12438175Sbrian    if (missed)
12538175Sbrian      log_Printf(LogCBCP, "%s: Timeout waiting for peer %s\n",
12638175Sbrian                 cbcp->p->dl->name, missed);
12738175Sbrian    datalink_CBCPFailed(cbcp->p->dl);
12838175Sbrian  }
12938175Sbrian}
13038175Sbrian
13138175Sbrianstatic void
13238175Sbriancbcp_StartTimer(struct cbcp *cbcp, int timeout)
13338175Sbrian{
13438175Sbrian  timer_Stop(&cbcp->fsm.timer);
13538175Sbrian  cbcp->fsm.timer.func = cbcp_Timeout;
13638175Sbrian  cbcp->fsm.timer.name = "cbcp";
13738175Sbrian  cbcp->fsm.timer.load = timeout * SECTICKS;
13838175Sbrian  cbcp->fsm.timer.arg = cbcp;
13938175Sbrian  timer_Start(&cbcp->fsm.timer);
14038175Sbrian}
14138175Sbrian
14238175Sbrian#define CBCP_CLOSED	(0)	/* Not in use */
14338175Sbrian#define CBCP_STOPPED	(1)	/* Waiting for a REQ */
14438175Sbrian#define CBCP_REQSENT	(2)	/* Waiting for a RESP */
14538175Sbrian#define CBCP_RESPSENT	(3)	/* Waiting for an ACK */
14638175Sbrian#define CBCP_ACKSENT	(4)	/* Waiting for an LCP Term REQ */
14738175Sbrian
14855146Sbrianstatic const char * const cbcpname[] = {
14938175Sbrian  "closed", "stopped", "req-sent", "resp-sent", "ack-sent"
15038175Sbrian};
15138175Sbrian
15238175Sbrianstatic const char *
153134789Sbriancbcpstate(unsigned s)
15438175Sbrian{
15538175Sbrian  if (s < sizeof cbcpname / sizeof cbcpname[0])
15638175Sbrian    return cbcpname[s];
15758034Sbrian  return HexStr(s, NULL, 0);
15838175Sbrian}
15938175Sbrian
16038175Sbrianstatic void
16138175Sbriancbcp_NewPhase(struct cbcp *cbcp, int new)
16238175Sbrian{
16338175Sbrian  if (cbcp->fsm.state != new) {
16438175Sbrian    log_Printf(LogCBCP, "%s: State change %s --> %s\n", cbcp->p->dl->name,
16538175Sbrian               cbcpstate(cbcp->fsm.state), cbcpstate(new));
16638175Sbrian    cbcp->fsm.state = new;
16738175Sbrian  }
16838175Sbrian}
16938175Sbrian
17038175Sbrianstruct cbcp_header {
17138175Sbrian  u_char code;
17238175Sbrian  u_char id;
17338175Sbrian  u_int16_t length;	/* Network byte order */
17438175Sbrian};
17538175Sbrian
17638175Sbrian
17738175Sbrian/* cbcp_header::code values */
17838175Sbrian#define CBCP_REQ	(1)
17938175Sbrian#define CBCP_RESPONSE	(2)
18038175Sbrian#define CBCP_ACK	(3)
18138175Sbrian
18238175Sbrianstruct cbcp_data {
18338175Sbrian  u_char type;
18438175Sbrian  u_char length;
18538175Sbrian  u_char delay;
18638175Sbrian  char addr_start[253];	/* max cbcp_data length 255 + 1 for NULL */
18738175Sbrian};
18838175Sbrian
18938175Sbrian/* cbcp_data::type values */
19038175Sbrian#define CBCP_NONUM	(1)
19138175Sbrian#define CBCP_CLIENTNUM	(2)
19238175Sbrian#define CBCP_SERVERNUM	(3)
19338175Sbrian#define CBCP_LISTNUM	(4)
19438175Sbrian
19538175Sbrianstatic void
19638175Sbriancbcp_Output(struct cbcp *cbcp, u_char code, struct cbcp_data *data)
19738175Sbrian{
19838175Sbrian  struct cbcp_header *head;
19938175Sbrian  struct mbuf *bp;
20038175Sbrian
20154912Sbrian  bp = m_get(sizeof *head + data->length, MB_CBCPOUT);
20238175Sbrian  head = (struct cbcp_header *)MBUF_CTOP(bp);
20338175Sbrian  head->code = code;
20438175Sbrian  head->id = cbcp->fsm.id;
20538175Sbrian  head->length = htons(sizeof *head + data->length);
20638175Sbrian  memcpy(MBUF_CTOP(bp) + sizeof *head, data, data->length);
20738175Sbrian  log_DumpBp(LogDEBUG, "cbcp_Output", bp);
20846686Sbrian  link_PushPacket(&cbcp->p->link, bp, cbcp->p->dl->bundle,
20950867Sbrian                  LINK_QUEUES(&cbcp->p->link) - 1, PROTO_CBCP);
21038175Sbrian}
21138175Sbrian
21238175Sbrianstatic const char *
213134789Sbriancbcp_data_Type(unsigned type)
21438175Sbrian{
21555146Sbrian  static const char * const types[] = {
21638175Sbrian    "No callback", "User-spec", "Server-spec", "list"
21738175Sbrian  };
21838175Sbrian
21938175Sbrian  if (type < 1 || type > sizeof types / sizeof types[0])
22058034Sbrian    return HexStr(type, NULL, 0);
22138175Sbrian  return types[type-1];
22238175Sbrian}
22338175Sbrian
22438175Sbrianstruct cbcp_addr {
22538175Sbrian  u_char type;
226141948Sbrian  char addr[sizeof ((struct cbcp_data *)0)->addr_start - 1];	/* ASCIIZ */
22738175Sbrian};
22838175Sbrian
22938175Sbrian/* cbcp_data::type values */
23038175Sbrian#define CBCP_ADDR_PSTN	(1)
23138175Sbrian
23238175Sbrianstatic void
23338175Sbriancbcp_data_Show(struct cbcp_data *data)
23438175Sbrian{
23538175Sbrian  struct cbcp_addr *addr;
23638175Sbrian  char *end;
23738175Sbrian
23838175Sbrian  addr = (struct cbcp_addr *)data->addr_start;
23938175Sbrian  end = (char *)data + data->length;
24038175Sbrian  *end = '\0';
24138175Sbrian
24238175Sbrian  log_Printf(LogCBCP, " TYPE %s\n", cbcp_data_Type(data->type));
24338175Sbrian  if ((char *)&data->delay < end) {
24438175Sbrian    log_Printf(LogCBCP, " DELAY %d\n", data->delay);
24538175Sbrian    while (addr->addr < end) {
24638175Sbrian      if (addr->type == CBCP_ADDR_PSTN)
24738175Sbrian        log_Printf(LogCBCP, " ADDR %s\n", addr->addr);
24838175Sbrian      else
24938175Sbrian        log_Printf(LogCBCP, " ADDR type %d ??\n", (int)addr->type);
25038175Sbrian      addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
25138175Sbrian    }
25238175Sbrian  }
25338175Sbrian}
25438175Sbrian
25538175Sbrianstatic void
25638175Sbriancbcp_SendReq(struct cbcp *cbcp)
25738175Sbrian{
25838175Sbrian  struct cbcp_data data;
25938175Sbrian  struct cbcp_addr *addr;
26038175Sbrian  char list[sizeof cbcp->fsm.phone], *next;
26138175Sbrian  int len, max;
26238175Sbrian
26338175Sbrian  /* Only callees send REQs */
26438175Sbrian
26538175Sbrian  log_Printf(LogCBCP, "%s: SendReq(%d) state = %s\n", cbcp->p->dl->name,
26638175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
26738175Sbrian  data.type = cbcp->fsm.type;
26838175Sbrian  data.delay = 0;
26938175Sbrian  strncpy(list, cbcp->fsm.phone, sizeof list - 1);
27038175Sbrian  list[sizeof list - 1] = '\0';
27138175Sbrian
27238175Sbrian  switch (data.type) {
27338175Sbrian    case CBCP_CLIENTNUM:
27438175Sbrian      addr = (struct cbcp_addr *)data.addr_start;
27538175Sbrian      addr->type = CBCP_ADDR_PSTN;
27638175Sbrian      *addr->addr = '\0';
27738175Sbrian      data.length = addr->addr - (char *)&data;
27838175Sbrian      break;
27938175Sbrian
28038175Sbrian    case CBCP_LISTNUM:
28138175Sbrian      addr = (struct cbcp_addr *)data.addr_start;
28238175Sbrian      for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
28338175Sbrian        len = strlen(next);
28438175Sbrian        max = data.addr_start + sizeof data.addr_start - addr->addr - 1;
28538175Sbrian        if (len <= max) {
28638175Sbrian          addr->type = CBCP_ADDR_PSTN;
287141948Sbrian          strncpy(addr->addr, next, sizeof addr->addr - 1);
288141948Sbrian          addr->addr[sizeof addr->addr - 1] = '\0';
28938175Sbrian          addr = (struct cbcp_addr *)((char *)addr + len + 2);
29038175Sbrian        } else
29138175Sbrian          log_Printf(LogWARN, "CBCP ADDR \"%s\" skipped - packet too large\n",
29238175Sbrian                     next);
29338175Sbrian      }
29438175Sbrian      data.length = (char *)addr - (char *)&data;
29538175Sbrian      break;
29638175Sbrian
29738175Sbrian    case CBCP_SERVERNUM:
29838175Sbrian      data.length = data.addr_start - (char *)&data;
29938175Sbrian      break;
30038175Sbrian
30138175Sbrian    default:
30240655Sbrian      data.length = (char *)&data.delay - (char *)&data;
30338175Sbrian      break;
30438175Sbrian  }
30538175Sbrian
30638175Sbrian  cbcp_data_Show(&data);
30738175Sbrian  cbcp_Output(cbcp, CBCP_REQ, &data);
30838175Sbrian  cbcp->fsm.restart--;
30938175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
31038175Sbrian  cbcp_NewPhase(cbcp, CBCP_REQSENT);		/* Wait for a RESPONSE */
31138175Sbrian}
31238175Sbrian
31338175Sbrianvoid
31438175Sbriancbcp_Up(struct cbcp *cbcp)
31538175Sbrian{
31638175Sbrian  struct lcp *lcp = &cbcp->p->link.lcp;
31738175Sbrian
31838175Sbrian  cbcp->fsm.delay = cbcp->p->dl->cfg.cbcp.delay;
31938175Sbrian  if (*cbcp->p->dl->peer.authname == '\0' ||
32038175Sbrian      !auth_SetPhoneList(cbcp->p->dl->peer.authname, cbcp->fsm.phone,
32138175Sbrian                         sizeof cbcp->fsm.phone)) {
32238175Sbrian    strncpy(cbcp->fsm.phone, cbcp->p->dl->cfg.cbcp.phone,
32338175Sbrian            sizeof cbcp->fsm.phone - 1);
32438175Sbrian    cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
32538175Sbrian  }
32638175Sbrian
32738175Sbrian  if (lcp->want_callback.opmask) {
32838175Sbrian    if (*cbcp->fsm.phone == '\0')
32938175Sbrian      cbcp->fsm.type = CBCP_NONUM;
33038175Sbrian    else if (!strcmp(cbcp->fsm.phone, "*")) {
33138175Sbrian      cbcp->fsm.type = CBCP_SERVERNUM;
33238175Sbrian      *cbcp->fsm.phone = '\0';
33338175Sbrian    } else
33438175Sbrian      cbcp->fsm.type = CBCP_CLIENTNUM;
33538175Sbrian    cbcp_NewPhase(cbcp, CBCP_STOPPED);		/* Wait for a REQ */
33644305Sbrian    cbcp_StartTimer(cbcp, cbcp->fsm.delay * DEF_FSMTRIES);
33738175Sbrian  } else {
33838175Sbrian    if (*cbcp->fsm.phone == '\0')
33938175Sbrian      cbcp->fsm.type = CBCP_NONUM;
34038175Sbrian    else if (!strcmp(cbcp->fsm.phone, "*")) {
34138175Sbrian      cbcp->fsm.type = CBCP_CLIENTNUM;
34238175Sbrian      *cbcp->fsm.phone = '\0';
34338175Sbrian    } else if (strchr(cbcp->fsm.phone, ','))
34438175Sbrian      cbcp->fsm.type = CBCP_LISTNUM;
34538175Sbrian    else
34638175Sbrian      cbcp->fsm.type = CBCP_SERVERNUM;
34744305Sbrian    cbcp->fsm.restart = DEF_FSMTRIES;
34838175Sbrian    cbcp_SendReq(cbcp);
34938175Sbrian  }
35038175Sbrian}
35138175Sbrian
35238175Sbrianstatic int
35338175Sbriancbcp_AdjustResponse(struct cbcp *cbcp, struct cbcp_data *data)
35438175Sbrian{
35538175Sbrian  /*
356228990Suqs   * We've received a REQ (data).  Adjust our response (cbcp->fsm.*)
35738175Sbrian   * so that we (hopefully) agree with the peer
35838175Sbrian   */
35938175Sbrian  struct cbcp_addr *addr;
36038175Sbrian
36138175Sbrian  switch (data->type) {
36238175Sbrian    case CBCP_NONUM:
36349132Sbrian      if (cbcp->p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))
36449132Sbrian        /*
36549132Sbrian         * if ``none'' is a configured callback possibility
36649132Sbrian         * (ie, ``set callback cbcp none''), go along with the callees
36749132Sbrian         * request
36849132Sbrian         */
36949132Sbrian        cbcp->fsm.type = CBCP_NONUM;
37049132Sbrian
37140798Sbrian      /*
37249132Sbrian       * Otherwise, we send our desired response anyway.  This seems to be
37349132Sbrian       * what Win95 does - although I can't find this behaviour documented
37449132Sbrian       * in the CBCP spec....
37540798Sbrian       */
37649132Sbrian
37740798Sbrian      return 1;
37838175Sbrian
37938175Sbrian    case CBCP_CLIENTNUM:
38038175Sbrian      if (cbcp->fsm.type == CBCP_CLIENTNUM) {
38138175Sbrian        char *ptr;
38238175Sbrian
38338175Sbrian        if (data->length > data->addr_start - (char *)data) {
38438175Sbrian          /*
38538175Sbrian           * The peer has given us an address type spec - make sure we
38638175Sbrian           * understand !
38738175Sbrian           */
38838175Sbrian          addr = (struct cbcp_addr *)data->addr_start;
38938175Sbrian          if (addr->type != CBCP_ADDR_PSTN) {
39038175Sbrian            log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
39138175Sbrian                       (int)addr->type);
39238175Sbrian            return 0;
39338175Sbrian          }
39438175Sbrian        }
39538175Sbrian        /* we accept the REQ even if the peer didn't specify an addr->type */
39638175Sbrian        ptr = strchr(cbcp->fsm.phone, ',');
39738175Sbrian        if (ptr)
39838175Sbrian          *ptr = '\0';		/* Just use the first number in our list */
39938175Sbrian        return 1;
40038175Sbrian      }
40138175Sbrian      log_Printf(LogPHASE, "CBCP: no number to pass to the peer !\n");
40238175Sbrian      return 0;
40338175Sbrian
40438175Sbrian    case CBCP_SERVERNUM:
40538175Sbrian      if (cbcp->fsm.type == CBCP_SERVERNUM) {
40638175Sbrian        *cbcp->fsm.phone = '\0';
40738175Sbrian        return 1;
40838175Sbrian      }
40938175Sbrian      if (data->length > data->addr_start - (char *)data) {
41038175Sbrian        /*
41138175Sbrian         * This violates the spec, but if the peer has told us the
41238175Sbrian         * number it wants to call back, take advantage of this fact
41338175Sbrian         * and allow things to proceed if we've specified the same
41438175Sbrian         * number
41538175Sbrian         */
41638175Sbrian        addr = (struct cbcp_addr *)data->addr_start;
41738175Sbrian        if (addr->type != CBCP_ADDR_PSTN) {
41838175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
41938175Sbrian                     (int)addr->type);
42038175Sbrian          return 0;
42138175Sbrian        } else if (cbcp->fsm.type == CBCP_CLIENTNUM) {
42238175Sbrian          /*
42338175Sbrian           * If the peer's insisting on deciding the number, make sure
42438175Sbrian           * it's one of the ones in our list.  If it is, let the peer
42538175Sbrian           * think it's in control :-)
42638175Sbrian           */
42738175Sbrian          char list[sizeof cbcp->fsm.phone], *next;
42838175Sbrian
42938175Sbrian          strncpy(list, cbcp->fsm.phone, sizeof list - 1);
43038175Sbrian          list[sizeof list - 1] = '\0';
43138175Sbrian          for (next = strtok(list, ","); next; next = strtok(NULL, ","))
43238175Sbrian            if (!strcmp(next, addr->addr)) {
43338175Sbrian              cbcp->fsm.type = CBCP_SERVERNUM;
43438175Sbrian              strcpy(cbcp->fsm.phone, next);
43538175Sbrian              return 1;
43638175Sbrian            }
43738175Sbrian        }
43838175Sbrian      }
43938175Sbrian      log_Printf(LogPHASE, "CBCP: Peer won't allow local decision !\n");
44038175Sbrian      return 0;
44138175Sbrian
44238175Sbrian    case CBCP_LISTNUM:
44338175Sbrian      if (cbcp->fsm.type == CBCP_CLIENTNUM || cbcp->fsm.type == CBCP_LISTNUM) {
44438175Sbrian        /*
44538175Sbrian         * Search through ``data''s addresses and see if cbcp->fsm.phone
44638175Sbrian         * contains any of them
44738175Sbrian         */
44838175Sbrian        char list[sizeof cbcp->fsm.phone], *next, *end;
44938175Sbrian
45038175Sbrian        addr = (struct cbcp_addr *)data->addr_start;
45138175Sbrian        end = (char *)data + data->length;
45238175Sbrian
45338175Sbrian        while (addr->addr < end) {
45438175Sbrian          if (addr->type == CBCP_ADDR_PSTN) {
45538175Sbrian            strncpy(list, cbcp->fsm.phone, sizeof list - 1);
45638175Sbrian            list[sizeof list - 1] = '\0';
45738175Sbrian            for (next = strtok(list, ","); next; next = strtok(NULL, ","))
45838175Sbrian              if (!strcmp(next, addr->addr)) {
45938175Sbrian                cbcp->fsm.type = CBCP_LISTNUM;
46038175Sbrian                strcpy(cbcp->fsm.phone, next);
46138175Sbrian                return 1;
46238175Sbrian              }
46338175Sbrian          } else
46438175Sbrian            log_Printf(LogCBCP, "Warning: Unrecognised address type %d !\n",
46538175Sbrian                       (int)addr->type);
46638175Sbrian          addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
46738175Sbrian        }
46838175Sbrian      }
46938175Sbrian      log_Printf(LogPHASE, "CBCP: no good number to pass to the peer !\n");
47038175Sbrian      return 0;
47138175Sbrian  }
47238175Sbrian
47338175Sbrian  log_Printf(LogCBCP, "Unrecognised REQ type %d !\n", (int)data->type);
47438175Sbrian  return 0;
47538175Sbrian}
47638175Sbrian
47738175Sbrianstatic void
47838175Sbriancbcp_SendResponse(struct cbcp *cbcp)
47938175Sbrian{
48038175Sbrian  struct cbcp_data data;
48138175Sbrian  struct cbcp_addr *addr;
48238175Sbrian
48338175Sbrian  /* Only callers send RESPONSEs */
48438175Sbrian
48538175Sbrian  log_Printf(LogCBCP, "%s: SendResponse(%d) state = %s\n", cbcp->p->dl->name,
48638175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
48738175Sbrian
48838175Sbrian  data.type = cbcp->fsm.type;
48938175Sbrian  data.delay = cbcp->fsm.delay;
49038175Sbrian  addr = (struct cbcp_addr *)data.addr_start;
49140655Sbrian  if (data.type == CBCP_NONUM)
49240655Sbrian    data.length = (char *)&data.delay - (char *)&data;
49340655Sbrian  else if (*cbcp->fsm.phone) {
49438175Sbrian    addr->type = CBCP_ADDR_PSTN;
495141948Sbrian    strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1);
496141948Sbrian    addr->addr[sizeof addr->addr - 1] = '\0';
49738175Sbrian    data.length = (addr->addr + strlen(addr->addr) + 1) - (char *)&data;
49838175Sbrian  } else
49938175Sbrian    data.length = data.addr_start - (char *)&data;
50038175Sbrian
50138175Sbrian  cbcp_data_Show(&data);
50238175Sbrian  cbcp_Output(cbcp, CBCP_RESPONSE, &data);
50338175Sbrian  cbcp->fsm.restart--;
50438175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
50538175Sbrian  cbcp_NewPhase(cbcp, CBCP_RESPSENT);	/* Wait for an ACK */
50638175Sbrian}
50738175Sbrian
50838175Sbrian/* What to do after checking an incoming response */
50938175Sbrian#define CBCP_ACTION_DOWN (0)
51038175Sbrian#define CBCP_ACTION_REQ (1)
51138175Sbrian#define CBCP_ACTION_ACK (2)
51238175Sbrian
51338175Sbrianstatic int
51438175Sbriancbcp_CheckResponse(struct cbcp *cbcp, struct cbcp_data *data)
51538175Sbrian{
51638175Sbrian  /*
51738175Sbrian   * We've received a RESPONSE (data).  Check if it agrees with
51838175Sbrian   * our REQ (cbcp->fsm)
51938175Sbrian   */
52038175Sbrian  struct cbcp_addr *addr;
52138175Sbrian
52238175Sbrian  addr = (struct cbcp_addr *)data->addr_start;
52338175Sbrian
52438175Sbrian  if (data->type == cbcp->fsm.type) {
52538175Sbrian    switch (cbcp->fsm.type) {
52638175Sbrian      case CBCP_NONUM:
52738175Sbrian        return CBCP_ACTION_ACK;
52838175Sbrian
52938175Sbrian      case CBCP_CLIENTNUM:
53038175Sbrian        if ((char *)data + data->length <= addr->addr)
53138175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
53238175Sbrian        else if (addr->type != CBCP_ADDR_PSTN)
53338175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
53438175Sbrian                     addr->type);
53538175Sbrian        else {
536141948Sbrian          strncpy(cbcp->fsm.phone, addr->addr, sizeof cbcp->fsm.phone - 1);
537141948Sbrian          cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
53838175Sbrian          cbcp->fsm.delay = data->delay;
53938175Sbrian          return CBCP_ACTION_ACK;
54038175Sbrian        }
54138175Sbrian        return CBCP_ACTION_DOWN;
54238175Sbrian
54338175Sbrian      case CBCP_SERVERNUM:
54438175Sbrian        cbcp->fsm.delay = data->delay;
54538175Sbrian        return CBCP_ACTION_ACK;
54638175Sbrian
54738175Sbrian      case CBCP_LISTNUM:
54838175Sbrian        if ((char *)data + data->length <= addr->addr)
54938175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
55038175Sbrian        else if (addr->type != CBCP_ADDR_PSTN)
55138175Sbrian          log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
55238175Sbrian                     addr->type);
55338175Sbrian        else {
55438175Sbrian          char list[sizeof cbcp->fsm.phone], *next;
55538175Sbrian
55638175Sbrian          strncpy(list, cbcp->fsm.phone, sizeof list - 1);
55738175Sbrian          list[sizeof list - 1] = '\0';
55838175Sbrian          for (next = strtok(list, ","); next; next = strtok(NULL, ","))
55938175Sbrian            if (!strcmp(addr->addr, next)) {
56038175Sbrian              strcpy(cbcp->fsm.phone, next);
56138175Sbrian              cbcp->fsm.delay = data->delay;
56238175Sbrian              return CBCP_ACTION_ACK;
56338175Sbrian            }
56438175Sbrian          log_Printf(LogPHASE, "CBCP: peer didn't respond with a "
56538175Sbrian                     "valid number !\n");
56638175Sbrian        }
56738175Sbrian        return CBCP_ACTION_DOWN;
56838175Sbrian    }
56996584Sbrian    log_Printf(LogPHASE, "Internal CBCP error - agreed on %d !\n",
57038175Sbrian               (int)cbcp->fsm.type);
57138175Sbrian    return CBCP_ACTION_DOWN;
57240484Sbrian  } else if (data->type == CBCP_NONUM && cbcp->fsm.type == CBCP_CLIENTNUM) {
57340484Sbrian    /*
57440484Sbrian     * Client doesn't want CBCP after all....
57540484Sbrian     * We only allow this when ``set cbcp *'' has been specified.
57640484Sbrian     */
57740484Sbrian    cbcp->fsm.type = CBCP_NONUM;
57840484Sbrian    return CBCP_ACTION_ACK;
57938175Sbrian  }
58038175Sbrian  log_Printf(LogCBCP, "Invalid peer RESPONSE\n");
58138175Sbrian  return CBCP_ACTION_REQ;
58238175Sbrian}
58338175Sbrian
58438175Sbrianstatic void
58538175Sbriancbcp_SendAck(struct cbcp *cbcp)
58638175Sbrian{
58738175Sbrian  struct cbcp_data data;
58842842Sbrian  struct cbcp_addr *addr;
58938175Sbrian
59038175Sbrian  /* Only callees send ACKs */
59138175Sbrian
59238175Sbrian  log_Printf(LogCBCP, "%s: SendAck(%d) state = %s\n", cbcp->p->dl->name,
59338175Sbrian             cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
59438175Sbrian
59538175Sbrian  data.type = cbcp->fsm.type;
59642842Sbrian  switch (data.type) {
59742842Sbrian    case CBCP_NONUM:
59842842Sbrian      data.length = (char *)&data.delay - (char *)&data;
59942842Sbrian      break;
60042842Sbrian    case CBCP_CLIENTNUM:
60142842Sbrian      addr = (struct cbcp_addr *)data.addr_start;
60242842Sbrian      addr->type = CBCP_ADDR_PSTN;
603141948Sbrian      strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1);
604141948Sbrian      addr->addr[sizeof addr->addr - 1] = '\0';
60542842Sbrian      data.delay = cbcp->fsm.delay;
60642842Sbrian      data.length = addr->addr + strlen(addr->addr) + 1 - (char *)&data;
60742842Sbrian      break;
60842842Sbrian    default:
60942842Sbrian      data.delay = cbcp->fsm.delay;
61042842Sbrian      data.length = data.addr_start - (char *)&data;
61142842Sbrian      break;
61242842Sbrian  }
61338175Sbrian
61438175Sbrian  cbcp_data_Show(&data);
61538175Sbrian  cbcp_Output(cbcp, CBCP_ACK, &data);
61638175Sbrian  cbcp->fsm.restart--;
61738175Sbrian  cbcp_StartTimer(cbcp, cbcp->fsm.delay);
61838175Sbrian  cbcp_NewPhase(cbcp, CBCP_ACKSENT);	/* Wait for an ACK */
61938175Sbrian}
62038175Sbrian
62146686Sbrianextern struct mbuf *
622134789Sbriancbcp_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp)
62338175Sbrian{
62446686Sbrian  struct physical *p = link2physical(l);
62538175Sbrian  struct cbcp_header *head;
62638175Sbrian  struct cbcp_data *data;
62738175Sbrian  struct cbcp *cbcp = &p->dl->cbcp;
628134789Sbrian  size_t len;
62938175Sbrian
63046686Sbrian  if (p == NULL) {
63146686Sbrian    log_Printf(LogERROR, "cbcp_Input: Not a physical link - dropped\n");
63254912Sbrian    m_freem(bp);
63346686Sbrian    return NULL;
63446686Sbrian  }
63546686Sbrian
63654912Sbrian  bp = m_pullup(bp);
63754912Sbrian  len = m_length(bp);
63838175Sbrian  if (len < sizeof(struct cbcp_header)) {
63954912Sbrian    m_freem(bp);
64046686Sbrian    return NULL;
64138175Sbrian  }
64238175Sbrian  head = (struct cbcp_header *)MBUF_CTOP(bp);
64338175Sbrian  if (ntohs(head->length) != len) {
644134833Smarcel    log_Printf(LogWARN, "Corrupt CBCP packet (code %d, length %u not %zu)"
64538175Sbrian               " - ignored\n", head->code, ntohs(head->length), len);
64654912Sbrian    m_freem(bp);
64746686Sbrian    return NULL;
64838175Sbrian  }
64954912Sbrian  m_settype(bp, MB_CBCPIN);
65038175Sbrian
65138175Sbrian  /* XXX check the id */
65238175Sbrian
65354912Sbrian  bp->m_offset += sizeof(struct cbcp_header);
65454912Sbrian  bp->m_len -= sizeof(struct cbcp_header);
65538175Sbrian  data = (struct cbcp_data *)MBUF_CTOP(bp);
65638175Sbrian
65738175Sbrian  switch (head->code) {
65838175Sbrian    case CBCP_REQ:
65938175Sbrian      log_Printf(LogCBCP, "%s: RecvReq(%d) state = %s\n",
66038175Sbrian                 p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
66138175Sbrian      cbcp_data_Show(data);
66238175Sbrian      if (cbcp->fsm.state == CBCP_STOPPED || cbcp->fsm.state == CBCP_RESPSENT) {
66338175Sbrian        timer_Stop(&cbcp->fsm.timer);
66438175Sbrian        if (cbcp_AdjustResponse(cbcp, data)) {
66544305Sbrian          cbcp->fsm.restart = DEF_FSMTRIES;
66640485Sbrian          cbcp->fsm.id = head->id;
66738175Sbrian          cbcp_SendResponse(cbcp);
66838175Sbrian        } else
66938175Sbrian          datalink_CBCPFailed(cbcp->p->dl);
67038175Sbrian      } else
67138175Sbrian        log_Printf(LogCBCP, "%s: unexpected REQ dropped\n", p->dl->name);
67238175Sbrian      break;
67338175Sbrian
67438175Sbrian    case CBCP_RESPONSE:
67538175Sbrian      log_Printf(LogCBCP, "%s: RecvResponse(%d) state = %s\n",
67638175Sbrian	         p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
67738175Sbrian      cbcp_data_Show(data);
67840486Sbrian      if (cbcp->fsm.id != head->id) {
67940486Sbrian        log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
68040486Sbrian                   cbcp->fsm.id, head->id);
68140486Sbrian        cbcp->fsm.id = head->id;
68240486Sbrian      }
68338175Sbrian      if (cbcp->fsm.state == CBCP_REQSENT || cbcp->fsm.state == CBCP_ACKSENT) {
68438175Sbrian        timer_Stop(&cbcp->fsm.timer);
68538175Sbrian        switch (cbcp_CheckResponse(cbcp, data)) {
68638175Sbrian          case CBCP_ACTION_REQ:
68738175Sbrian            cbcp_SendReq(cbcp);
68838175Sbrian            break;
68938175Sbrian
69038175Sbrian          case CBCP_ACTION_ACK:
69144305Sbrian            cbcp->fsm.restart = DEF_FSMTRIES;
69238175Sbrian            cbcp_SendAck(cbcp);
69338175Sbrian            if (cbcp->fsm.type == CBCP_NONUM) {
69438175Sbrian              /*
69538175Sbrian               * Don't change state in case the peer doesn't get our ACK,
69638175Sbrian               * just bring the layer up.
69738175Sbrian               */
69838175Sbrian              timer_Stop(&cbcp->fsm.timer);
69938175Sbrian              datalink_NCPUp(cbcp->p->dl);
70038175Sbrian            }
70138175Sbrian            break;
70238175Sbrian
70338175Sbrian          default:
70438175Sbrian            datalink_CBCPFailed(cbcp->p->dl);
70538175Sbrian            break;
70638175Sbrian        }
70738175Sbrian      } else
70838175Sbrian        log_Printf(LogCBCP, "%s: unexpected RESPONSE dropped\n", p->dl->name);
70938175Sbrian      break;
71038175Sbrian
71138175Sbrian    case CBCP_ACK:
71238175Sbrian      log_Printf(LogCBCP, "%s: RecvAck(%d) state = %s\n",
71338175Sbrian	         p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
71438175Sbrian      cbcp_data_Show(data);
71540486Sbrian      if (cbcp->fsm.id != head->id) {
71640486Sbrian        log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
71740486Sbrian                   cbcp->fsm.id, head->id);
71840486Sbrian        cbcp->fsm.id = head->id;
71940486Sbrian      }
72049132Sbrian      if (cbcp->fsm.type == CBCP_NONUM) {
72149132Sbrian        /*
72249132Sbrian         * Don't change state in case the peer doesn't get our ACK,
72349132Sbrian         * just bring the layer up.
72449132Sbrian         */
72538175Sbrian        timer_Stop(&cbcp->fsm.timer);
72649132Sbrian        datalink_NCPUp(cbcp->p->dl);
72749132Sbrian      } else if (cbcp->fsm.state == CBCP_RESPSENT) {
72849132Sbrian        timer_Stop(&cbcp->fsm.timer);
72938175Sbrian        datalink_CBCPComplete(cbcp->p->dl);
73038175Sbrian        log_Printf(LogPHASE, "%s: CBCP: Peer will dial back\n", p->dl->name);
73138175Sbrian      } else
73238175Sbrian        log_Printf(LogCBCP, "%s: unexpected ACK dropped\n", p->dl->name);
73338175Sbrian      break;
73438175Sbrian
73538175Sbrian    default:
736134833Smarcel      log_Printf(LogWARN, "Unrecognised CBCP packet (code %d, length %zd)\n",
73738175Sbrian               head->code, len);
73838175Sbrian      break;
73938175Sbrian  }
74038175Sbrian
74154912Sbrian  m_freem(bp);
74246686Sbrian  return NULL;
74338175Sbrian}
74438175Sbrian
74538175Sbrianvoid
74638175Sbriancbcp_Down(struct cbcp *cbcp)
74738175Sbrian{
74838175Sbrian  timer_Stop(&cbcp->fsm.timer);
74938175Sbrian  cbcp_NewPhase(cbcp, CBCP_CLOSED);
75038175Sbrian  cbcp->required = 0;
75138175Sbrian}
75238175Sbrian
75338175Sbrianvoid
75438175Sbriancbcp_ReceiveTerminateReq(struct physical *p)
75538175Sbrian{
75638175Sbrian  if (p->dl->cbcp.fsm.state == CBCP_ACKSENT) {
75738175Sbrian    /* Don't change our state in case the peer doesn't get the ACK */
75838175Sbrian    p->dl->cbcp.required = 1;
75938175Sbrian    log_Printf(LogPHASE, "%s: CBCP: Will dial back on %s\n", p->dl->name,
76038175Sbrian               p->dl->cbcp.fsm.phone);
76138175Sbrian  } else
76238175Sbrian    cbcp_NewPhase(&p->dl->cbcp, CBCP_CLOSED);
76338175Sbrian}
764