diff options
Diffstat (limited to 'usr.sbin/ppp/cbcp.c')
| -rw-r--r-- | usr.sbin/ppp/cbcp.c | 763 | 
1 files changed, 763 insertions, 0 deletions
| diff --git a/usr.sbin/ppp/cbcp.c b/usr.sbin/ppp/cbcp.c new file mode 100644 index 000000000000..bc92ca40efd5 --- /dev/null +++ b/usr.sbin/ppp/cbcp.c @@ -0,0 +1,763 @@ +/*- + * 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. + */ + +#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); +} | 
