text: /*-
   * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
   *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
   *                           Internet Initiative Japan, Inc (IIJ)
   * 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.
   *
   * $OpenBSD: lcp.c,v 1.43 2009/12/11 18:15:17 oga Exp $
   */
  
  #include <sys/param.h>
  #include <netinet/in.h>
  #include <netinet/in_systm.h>
  #include <netinet/ip.h>
  #include <sys/socket.h>
  #include <sys/un.h>
  
  #include <signal.h>
  #include <stdarg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <termios.h>
  #include <unistd.h>
  
  #include "layer.h"
  #include "ua.h"
  #include "defs.h"
  #include "command.h"
  #include "mbuf.h"
  #include "log.h"
  #include "timer.h"
  #include "fsm.h"
  #include "iplist.h"
  #include "throughput.h"
  #include "proto.h"
  #include "descriptor.h"
  #include "lqr.h"
  #include "hdlc.h"
  #include "lcp.h"
  #include "ccp.h"
  #include "async.h"
  #include "link.h"
  #include "physical.h"
  #include "prompt.h"
  #include "slcompress.h"
  #include "ncpaddr.h"
  #include "ipcp.h"
  #include "filter.h"
  #include "mp.h"
  #include "chat.h"
  #include "auth.h"
  #include "chap.h"
  #include "cbcp.h"
  #include "datalink.h"
  #ifndef NORADIUS
  #include "radius.h"
  #endif
  #include "ipv6cp.h"
  #include "ncp.h"
  #include "bundle.h"
  
  /* for received LQRs */
  struct lqrreq {
    struct fsm_opt_hdr hdr;
    u_short proto;		/* Quality protocol */
    u_int32_t period;		/* Reporting interval */
  };
  
  static int LcpLayerUp(struct fsm *);
  static void LcpLayerDown(struct fsm *);
  static void LcpLayerStart(struct fsm *);
  static void LcpLayerFinish(struct fsm *);
  static void LcpInitRestartCounter(struct fsm *, int);
  static void LcpSendConfigReq(struct fsm *);
  static void LcpSentTerminateReq(struct fsm *);
  static void LcpSendTerminateAck(struct fsm *, u_char);
  static void LcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
                              struct fsm_decode *);
  
  static struct fsm_callbacks lcp_Callbacks = {
    LcpLayerUp,
    LcpLayerDown,
    LcpLayerStart,
    LcpLayerFinish,
    LcpInitRestartCounter,
    LcpSendConfigReq,
    LcpSentTerminateReq,
    LcpSendTerminateAck,
    LcpDecodeConfig,
    fsm_NullRecvResetReq,
    fsm_NullRecvResetAck
  };
  
  static const char * const lcp_TimerNames[] =
    {"LCP restart", "LCP openmode", "LCP stopped"};
  
  static const char *
  protoname(int proto)
  {
    static const char * const cftypes[] = {
      /* Check out the latest ``Assigned numbers'' rfc (1700) */
      NULL,
      "MRU",		/* 1: Maximum-Receive-Unit */
      "ACCMAP",		/* 2: Async-Control-Character-Map */
      "AUTHPROTO",	/* 3: Authentication-Protocol */
      "QUALPROTO",	/* 4: Quality-Protocol */
      "MAGICNUM",		/* 5: Magic-Number */
      "RESERVED",		/* 6: RESERVED */
      "PROTOCOMP",	/* 7: Protocol-Field-Compression */
      "ACFCOMP",		/* 8: Address-and-Control-Field-Compression */
      "FCSALT",		/* 9: FCS-Alternatives */
      "SDP",		/* 10: Self-Describing-Pad */
      "NUMMODE",		/* 11: Numbered-Mode */
      "MULTIPROC",	/* 12: Multi-Link-Procedure */
      "CALLBACK",		/* 13: Callback */
      "CONTIME",		/* 14: Connect-Time */
      "COMPFRAME",	/* 15: Compound-Frames */
      "NDE",		/* 16: Nominal-Data-Encapsulation */
      "MRRU",		/* 17: Multilink-MRRU */
      "SHORTSEQ",		/* 18: Multilink-Short-Sequence-Number-Header */
      "ENDDISC",		/* 19: Multilink-Endpoint-Discriminator */
      "PROPRIETRY",	/* 20: Proprietary */
      "DCEID",		/* 21: DCE-Identifier */
      "MULTIPP",		/* 22: Multi-Link-Plus-Procedure */
      "LDBACP",		/* 23: Link Discriminator for BACP */
    };
  
    if (proto < 0 || proto >= sizeof cftypes / sizeof *cftypes ||
        cftypes[proto] == NULL)
      return HexStr(proto, NULL, 0);
  
    return cftypes[proto];
  }
  
  int
  lcp_ReportStatus(struct cmdargs const *arg)
  {
    struct link *l;
    struct lcp *lcp;
  
    l = command_ChooseLink(arg);
    lcp = &l->lcp;
  
    prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, lcp->fsm.name,
                  State2Nam(lcp->fsm.state));
    prompt_Printf(arg->prompt,
                  " his side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
                  "           MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
                  lcp->his_mru, (u_long)lcp->his_accmap,
                  lcp->his_protocomp ? "on" : "off",
                  lcp->his_acfcomp ? "on" : "off",
                  (u_long)lcp->his_magic, lcp->his_mrru,
                  lcp->his_shortseq ? "on" : "off", lcp->his_reject);
    prompt_Printf(arg->prompt,
                  " my  side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
                  "           MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
                  lcp->want_mru, (u_long)lcp->want_accmap,
                  lcp->want_protocomp ? "on" : "off",
                  lcp->want_acfcomp ? "on" : "off",
                  (u_long)lcp->want_magic, lcp->want_mrru,
                  lcp->want_shortseq ? "on" : "off", lcp->my_reject);
  
    if (lcp->cfg.mru)
      prompt_Printf(arg->prompt, "\n Defaults: MRU = %d (max %d), ",
                    lcp->cfg.mru, lcp->cfg.max_mru);
    else
      prompt_Printf(arg->prompt, "\n Defaults: MRU = any (max %d), ",
                    lcp->cfg.max_mru);
    if (lcp->cfg.mtu)
      prompt_Printf(arg->prompt, "MTU = %d (max %d), ",
                    lcp->cfg.mtu, lcp->cfg.max_mtu);
    else
      prompt_Printf(arg->prompt, "MTU = any (max %d), ", lcp->cfg.max_mtu);
    prompt_Printf(arg->prompt, "ACCMAP = %08lx\n", (u_long)lcp->cfg.accmap);
    prompt_Printf(arg->prompt, "           LQR period = %us, ",
                  lcp->cfg.lqrperiod);
    prompt_Printf(arg->prompt, "Open Mode = %s",
                  lcp->cfg.openmode == OPEN_PASSIVE ? "passive" : "active");
    if (lcp->cfg.openmode > 0)
      prompt_Printf(arg->prompt, " (delay %ds)", lcp->cfg.openmode);
    prompt_Printf(arg->prompt, "\n           FSM retry = %us, max %u Config"
                  " REQ%s, %u Term REQ%s\n", lcp->cfg.fsm.timeout,
                  lcp->cfg.fsm.maxreq, lcp->cfg.fsm.maxreq == 1 ? "" : "s",
                  lcp->cfg.fsm.maxtrm, lcp->cfg.fsm.maxtrm == 1 ? "" : "s");
    prompt_Printf(arg->prompt, "    Ident: %s\n", lcp->cfg.ident);
    prompt_Printf(arg->prompt, "\n Negotiation:\n");
    prompt_Printf(arg->prompt, "           ACFCOMP =   %s\n",
                  command_ShowNegval(lcp->cfg.acfcomp));
    prompt_Printf(arg->prompt, "           CHAP =      %s\n",
                  command_ShowNegval(lcp->cfg.chap05));
  #ifndef NODES
    prompt_Printf(arg->prompt, "           CHAP80 =    %s\n",
                  command_ShowNegval(lcp->cfg.chap80nt));
    prompt_Printf(arg->prompt, "           LANMan =    %s\n",
                  command_ShowNegval(lcp->cfg.chap80lm));
    prompt_Printf(arg->prompt, "           CHAP81 =    %s\n",
                  command_ShowNegval(lcp->cfg.chap81));
  #endif
    prompt_Printf(arg->prompt, "           LQR =       %s\n",
                  command_ShowNegval(lcp->cfg.lqr));
    prompt_Printf(arg->prompt, "           LCP ECHO =  %s\n",
                  lcp->cfg.echo ? "enabled" : "disabled");
    prompt_Printf(arg->prompt, "           PAP =       %s\n",
                  command_ShowNegval(lcp->cfg.pap));
    prompt_Printf(arg->prompt, "           PROTOCOMP = %s\n",
                  command_ShowNegval(lcp->cfg.protocomp));
  
    return 0;
  }
  
  static u_int32_t
  GenerateMagic(void)
  {
    /* Generate random number which will be used as magic number */
    randinit();
    return random();
  }
  
  void
  lcp_SetupCallbacks(struct lcp *lcp)
  {
    lcp->fsm.fn = &lcp_Callbacks;
    lcp->fsm.FsmTimer.name = lcp_TimerNames[0];
    lcp->fsm.OpenTimer.name = lcp_TimerNames[1];
    lcp->fsm.StoppedTimer.name = lcp_TimerNames[2];
  }
  
  void
  lcp_Init(struct lcp *lcp, struct bundle *bundle, struct link *l,
           const struct fsm_parent *parent)
  {
    /* Initialise ourselves */
    int mincode = parent ? 1 : LCP_MINMPCODE;
  
    fsm_Init(&lcp->fsm, "LCP", PROTO_LCP, mincode, LCP_MAXCODE, LogLCP,
             bundle, l, parent, &lcp_Callbacks, lcp_TimerNames);
  
    lcp->cfg.mru = 0;
    lcp->cfg.max_mru = MAX_MRU;
    lcp->cfg.mtu = 0;
    lcp->cfg.max_mtu = MAX_MTU;
    lcp->cfg.accmap = 0;
    lcp->cfg.openmode = 1;
    lcp->cfg.lqrperiod = DEF_LQRPERIOD;
    lcp->cfg.fsm.timeout = DEF_FSMRETRY;
    lcp->cfg.fsm.maxreq = DEF_FSMTRIES;
    lcp->cfg.fsm.maxtrm = DEF_FSMTRIES;
  
    lcp->cfg.acfcomp = NEG_ENABLED|NEG_ACCEPTED;
    lcp->cfg.chap05 = NEG_ACCEPTED;
  #ifndef NODES
    lcp->cfg.chap80nt = NEG_ACCEPTED;
    lcp->cfg.chap80lm = 0;
    lcp->cfg.chap81 = NEG_ACCEPTED;
  #endif
    lcp->cfg.lqr = NEG_ACCEPTED;
    lcp->cfg.echo = 0;
    lcp->cfg.pap = NEG_ACCEPTED;
    lcp->cfg.protocomp = NEG_ENABLED|NEG_ACCEPTED;
    *lcp->cfg.ident = '\0';
  
    lcp_Setup(lcp, lcp->cfg.openmode);
  }
  
  void
  lcp_Setup(struct lcp *lcp, int openmode)
  {
    struct physical *p = link2physical(lcp->fsm.link);
  
    lcp->fsm.open_mode = openmode;
  
    lcp->his_mru = DEF_MRU;
    lcp->his_mrru = 0;
    lcp->his_magic = 0;
    lcp->his_lqrperiod = 0;
    lcp->his_acfcomp = 0;
    lcp->his_auth = 0;
    lcp->his_authtype = 0;
    lcp->his_callback.opmask = 0;
    lcp->his_shortseq = 0;
    lcp->mru_req = 0;
  
    if ((lcp->want_mru = lcp->cfg.mru) == 0)
      lcp->want_mru = DEF_MRU;
    lcp->want_mrru = lcp->fsm.bundle->ncp.mp.cfg.mrru;
    lcp->want_shortseq = IsEnabled(lcp->fsm.bundle->ncp.mp.cfg.shortseq) ? 1 : 0;
    lcp->want_acfcomp = IsEnabled(lcp->cfg.acfcomp) ? 1 : 0;
  
    if (lcp->fsm.parent) {
      lcp->his_accmap = 0xffffffff;
      lcp->want_accmap = lcp->cfg.accmap;
      lcp->his_protocomp = 0;
      lcp->want_protocomp = IsEnabled(lcp->cfg.protocomp) ? 1 : 0;
      lcp->want_magic = GenerateMagic();
  
      if (IsEnabled(lcp->cfg.chap05)) {
        lcp->want_auth = PROTO_CHAP;
        lcp->want_authtype = 0x05;
  #ifndef NODES
      } else if (IsEnabled(lcp->cfg.chap80nt) ||
                 IsEnabled(lcp->cfg.chap80lm)) {
        lcp->want_auth = PROTO_CHAP;
        lcp->want_authtype = 0x80;
      } else if (IsEnabled(lcp->cfg.chap81)) {
        lcp->want_auth = PROTO_CHAP;
        lcp->want_authtype = 0x81;
  #endif
      } else if (IsEnabled(lcp->cfg.pap)) {
        lcp->want_auth = PROTO_PAP;
        lcp->want_authtype = 0;
      } else {
        lcp->want_auth = 0;
        lcp->want_authtype = 0;
      }
  
      if (p->type != PHYS_DIRECT)
        memcpy(&lcp->want_callback, &p->dl->cfg.callback,
               sizeof(struct callback));
      else
        lcp->want_callback.opmask = 0;
      lcp->want_lqrperiod = IsEnabled(lcp->cfg.lqr) ?
                            lcp->cfg.lqrperiod * 100 : 0;
    } else {
      lcp->his_accmap = lcp->want_accmap = 0;
      lcp->his_protocomp = lcp->want_protocomp = 1;
      lcp->want_magic = 0;
      lcp->want_auth = 0;
      lcp->want_authtype = 0;
      lcp->want_callback.opmask = 0;
      lcp->want_lqrperiod = 0;
    }
  
    lcp->his_reject = lcp->my_reject = 0;
    lcp->auth_iwait = lcp->auth_ineed = 0;
    lcp->LcpFailedMagic = 0;
  }
  
  static void
  LcpInitRestartCounter(struct fsm *fp, int what)
  {
    /* Set fsm timer load */
    struct lcp *lcp = fsm2lcp(fp);
  
    fp->FsmTimer.load = lcp->cfg.fsm.timeout * SECTICKS;
    switch (what) {
      case FSM_REQ_TIMER:
        fp->restart = lcp->cfg.fsm.maxreq;
        break;
      case FSM_TRM_TIMER:
        fp->restart = lcp->cfg.fsm.maxtrm;
        break;
      default:
        fp->restart = 1;
        break;
    }
  }
  
  static void
  LcpSendConfigReq(struct fsm *fp)
  {
    /* Send config REQ please */
    struct physical *p = link2physical(fp->link);
    struct lcp *lcp = fsm2lcp(fp);
    u_char buff[200];
    struct fsm_opt *o;
    struct mp *mp;
    u_int16_t proto;
    u_short maxmru;
  
    if (!p) {
      log_Printf(LogERROR, "%s: LcpSendConfigReq: Not a physical link !\n",
                fp->link->name);
      return;
    }
  
    o = (struct fsm_opt *)buff;
    if (!physical_IsSync(p)) {
      if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP))
        INC_FSM_OPT(TY_ACFCOMP, 2, o);
  
      if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP))
        INC_FSM_OPT(TY_PROTOCOMP, 2, o);
  
      if (!REJECTED(lcp, TY_ACCMAP)) {
        ua_htonl(&lcp->want_accmap, o->data);
        INC_FSM_OPT(TY_ACCMAP, 6, o);
      }
    }
  
    maxmru = p ? physical_DeviceMTU(p) : 0;
    if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
      maxmru = lcp->cfg.max_mru;
    if (maxmru && lcp->want_mru > maxmru) {
      log_Printf(LogWARN, "%s: Reducing configured MRU from %u to %u\n",
                 fp->link->name, lcp->want_mru, maxmru);
      lcp->want_mru = maxmru;
    }
    if (!REJECTED(lcp, TY_MRU)) {
      ua_htons(&lcp->want_mru, o->data);
      INC_FSM_OPT(TY_MRU, 4, o);
    }
  
    if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) {
      ua_htonl(&lcp->want_magic, o->data);
      INC_FSM_OPT(TY_MAGICNUM, 6, o);
    }
  
    if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) {
      proto = PROTO_LQR;
      ua_htons(&proto, o->data);
      ua_htonl(&lcp->want_lqrperiod, o->data + 2);
      INC_FSM_OPT(TY_QUALPROTO, 8, o);
    }
  
    switch (lcp->want_auth) {
    case PROTO_PAP:
      proto = PROTO_PAP;
      ua_htons(&proto, o->data);
      INC_FSM_OPT(TY_AUTHPROTO, 4, o);
      break;
  
    case PROTO_CHAP:
      proto = PROTO_CHAP;
      ua_htons(&proto, o->data);
      o->data[2] = lcp->want_authtype;
      INC_FSM_OPT(TY_AUTHPROTO, 5, o);
      break;
    }
  
    if (!REJECTED(lcp, TY_CALLBACK)) {
      if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
        *o->data = CALLBACK_AUTH;
        INC_FSM_OPT(TY_CALLBACK, 3, o);
      } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
        *o->data = CALLBACK_CBCP;
        INC_FSM_OPT(TY_CALLBACK, 3, o);
      } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
        int sz = strlen(lcp->want_callback.msg);
  
        if (sz > sizeof o->data - 1) {
          sz = sizeof o->data - 1;
          log_Printf(LogWARN, "Truncating E164 data to %d octets (oops!)\n", sz);
        }
        *o->data = CALLBACK_E164;
        memcpy(o->data + 1, lcp->want_callback.msg, sz);
        INC_FSM_OPT(TY_CALLBACK, sz + 3, o);
      }
    }
  
    if (lcp->want_mrru && !REJECTED(lcp, TY_MRRU)) {
      ua_htons(&lcp->want_mrru, o->data);
      INC_FSM_OPT(TY_MRRU, 4, o);
  
      if (lcp->want_shortseq && !REJECTED(lcp, TY_SHORTSEQ))
        INC_FSM_OPT(TY_SHORTSEQ, 2, o);
    }
  
    mp = &lcp->fsm.bundle->ncp.mp;
    if (mp->cfg.enddisc.class != 0 && IsEnabled(mp->cfg.negenddisc) &&
        !REJECTED(lcp, TY_ENDDISC)) {
      *o->data = mp->cfg.enddisc.class;
      memcpy(o->data+1, mp->cfg.enddisc.address, mp->cfg.enddisc.len);
      INC_FSM_OPT(TY_ENDDISC, mp->cfg.enddisc.len + 3, o);
    }
  
    fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
               MB_LCPOUT);
  }
  
  void
  lcp_SendProtoRej(struct lcp *lcp, u_char *option, int count)
  {
    /* Don't understand `option' */
    fsm_Output(&lcp->fsm, CODE_PROTOREJ, lcp->fsm.reqid, option, count,
               MB_LCPOUT);
  }
  
  int
  lcp_SendIdentification(struct lcp *lcp)
  {
    static u_char id;		/* Use a private id */
    u_char msg[DEF_MRU - 3];
    const char *argv[2];
    char *exp[2];
  
    if (*lcp->cfg.ident == '\0')
      return 0;
  
    argv[0] = lcp->cfg.ident;
    argv[1] = NULL;
  
    command_Expand(exp, 1, argv, lcp->fsm.bundle, 1, getpid());
  
    ua_htonl(&lcp->want_magic, msg);
    strncpy(msg + 4, exp[0], sizeof msg - 5);
    msg[sizeof msg - 1] = '\0';
  
    fsm_Output(&lcp->fsm, CODE_IDENT, id++, msg, 4 + strlen(msg + 4), MB_LCPOUT);
    log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->want_magic);
    log_Printf(LogLCP, " TEXT %s\n", msg + 4);
  
    command_Free(1, exp);
    return 1;
  }
  
  void
  lcp_RecvIdentification(struct lcp *lcp, char *data)
  {
    log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->his_magic);
    log_Printf(LogLCP, " TEXT %s\n", data);
  }
  
  static void
  LcpSentTerminateReq(struct fsm *fp)
  {
    /* Term REQ just sent by FSM */
  }
  
  static void
  LcpSendTerminateAck(struct fsm *fp, u_char id)
  {
    /* Send Term ACK please */
    struct physical *p = link2physical(fp->link);
  
    if (p && p->dl->state == DATALINK_CBCP)
      cbcp_ReceiveTerminateReq(p);
  
    fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_LCPOUT);
  }
  
  static void
  LcpLayerStart(struct fsm *fp)
  {
    /* We're about to start up ! */
    struct lcp *lcp = fsm2lcp(fp);
  
    log_Printf(LogLCP, "%s: LayerStart\n", fp->link->name);
    lcp->LcpFailedMagic = 0;
    fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
    lcp->mru_req = 0;
  }
  
  static void
  LcpLayerFinish(struct fsm *fp)
  {
    /* We're now down */
    log_Printf(LogLCP, "%s: LayerFinish\n", fp->link->name);
  }
  
  static int
  LcpLayerUp(struct fsm *fp)
  {
    /* We're now up */
    struct physical *p = link2physical(fp->link);
    struct lcp *lcp = fsm2lcp(fp);
  
    log_Printf(LogLCP, "%s: LayerUp\n", fp->link->name);
    physical_SetAsyncParams(p, lcp->want_accmap, lcp->his_accmap);
    lqr_Start(lcp);
    hdlc_StartTimer(&p->hdlc);
    fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
  
    lcp_SendIdentification(lcp);
  
    return 1;
  }
  
  static void
  LcpLayerDown(struct fsm *fp)
  {
    /* About to come down */
    struct physical *p = link2physical(fp->link);
  
    log_Printf(LogLCP, "%s: LayerDown\n", fp->link->name);
    hdlc_StopTimer(&p->hdlc);
    lqr_StopTimer(p);
    lcp_Setup(fsm2lcp(fp), 0);
  }
  
  static int
  E164ok(struct callback *cb, char *req, int sz)
  {
    char list[sizeof cb->msg], *next;
    int len;
  
    if (!strcmp(cb->msg, "*"))
      return 1;
  
    strncpy(list, cb->msg, sizeof list - 1);
    list[sizeof list - 1] = '\0';
    for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
      len = strlen(next);
      if (sz == len && !memcmp(list, req, sz))
        return 1;
    }
    return 0;
  }
  
  static int
  lcp_auth_nak(struct lcp *lcp, struct fsm_decode *dec)
  {
    struct fsm_opt nak;
  
    nak.hdr.id = TY_AUTHPROTO;
  
    if (IsAccepted(lcp->cfg.pap)) {
      nak.hdr.len = 4;
      nak.data[0] = (unsigned char)(PROTO_PAP >> 8);
      nak.data[1] = (unsigned char)PROTO_PAP;
      fsm_nak(dec, &nak);
      return 1;
    }
  
    nak.hdr.len = 5;
    nak.data[0] = (unsigned char)(PROTO_CHAP >> 8);
    nak.data[1] = (unsigned char)PROTO_CHAP;
  
    if (IsAccepted(lcp->cfg.chap05)) {
      nak.data[2] = 0x05;
      fsm_nak(dec, &nak);
  #ifndef NODES
    } else if (IsAccepted(lcp->cfg.chap80nt) ||
               IsAccepted(lcp->cfg.chap80lm)) {
      nak.data[2] = 0x80;
      fsm_nak(dec, &nak);
    } else if (IsAccepted(lcp->cfg.chap81)) {
      nak.data[2] = 0x81;
      fsm_nak(dec, &nak);
  #endif
    } else {
      return 0;
    }
  
    return 1;
  }
  
  static void
  LcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
                  struct fsm_decode *dec)
  {
    /* Deal with incoming PROTO_LCP */
    struct lcp *lcp = fsm2lcp(fp);
    int sz, pos, op, callback_req, chap_type;
    u_int32_t magic, accmap;
    u_short mru, phmtu, maxmtu, maxmru, wantmtu, wantmru, proto;
    struct lqrreq *req, reqbuff;
    char request[20], desc[22];
    struct mp *mp;
    struct physical *p = link2physical(fp->link);
    struct fsm_opt *opt, nak;
  
    sz = op = callback_req = 0;
  
    while (end - cp >= sizeof(opt->hdr)) {
      if ((opt = fsm_readopt(&cp)) == NULL)
        break;
  
      snprintf(request, sizeof request, " %s[%d]", protoname(opt->hdr.id),
               opt->hdr.len);
  
      switch (opt->hdr.id) {
      case TY_MRRU:
        mp = &lcp->fsm.bundle->ncp.mp;
        ua_ntohs(opt->data, &mru);
        log_Printf(LogLCP, "%s %u\n", request, mru);
  
        switch (mode_type) {
        case MODE_REQ:
          if (mp->cfg.mrru) {
            if (REJECTED(lcp, TY_MRRU))
              /* Ignore his previous reject so that we REQ next time */
              lcp->his_reject &= ~(1 << opt->hdr.id);
  
            if (mru > MAX_MRU) {
              /* Push him down to MAX_MRU */
              lcp->his_mrru = MAX_MRU;
              nak.hdr.id = TY_MRRU;
              nak.hdr.len = 4;
              ua_htons(&lcp->his_mrru, nak.data);
              fsm_nak(dec, &nak);
            } else if (mru < MIN_MRU) {
              /* Push him up to MIN_MRU */
              lcp->his_mrru = MIN_MRU;
              nak.hdr.id = TY_MRRU;
              nak.hdr.len = 4;
              ua_htons(&lcp->his_mrru, nak.data);
              fsm_nak(dec, &nak);
            } else {
              lcp->his_mrru = mru;
              fsm_ack(dec, opt);
            }
            break;
          } else {
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          }
          break;
        case MODE_NAK:
          if (mp->cfg.mrru) {
            if (REJECTED(lcp, TY_MRRU))
              /* Must have changed his mind ! */
              lcp->his_reject &= ~(1 << opt->hdr.id);
  
            if (mru > MAX_MRU)
              lcp->want_mrru = MAX_MRU;
            else if (mru < MIN_MRU)
              lcp->want_mrru = MIN_MRU;
            else
              lcp->want_mrru = mru;
          }
          /* else we honour our config and don't send the suggested REQ */
          break;
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          lcp->want_mrru = 0;		/* Ah well, no multilink :-( */
          break;
        }
        break;
  
      case TY_MRU:
        lcp->mru_req = 1;
        ua_ntohs(opt->data, &mru);
        log_Printf(LogLCP, "%s %d\n", request, mru);
  
        switch (mode_type) {
        case MODE_REQ:
          maxmtu = p ? physical_DeviceMTU(p) : 0;
          if (lcp->cfg.max_mtu && (!maxmtu || maxmtu > lcp->cfg.max_mtu))
            maxmtu = lcp->cfg.max_mtu;
          wantmtu = lcp->cfg.mtu;
          if (maxmtu && wantmtu > maxmtu) {
            log_Printf(LogWARN, "%s: Reducing configured MTU from %u to %u\n",
                       fp->link->name, wantmtu, maxmtu);
            wantmtu = maxmtu;
          }
  
          if (maxmtu && mru > maxmtu) {
            lcp->his_mru = maxmtu;
            nak.hdr.id = TY_MRU;
            nak.hdr.len = 4;
            ua_htons(&lcp->his_mru, nak.data);
            fsm_nak(dec, &nak);
          } else if (wantmtu && mru < wantmtu) {
            /* Push him up to MTU or MIN_MRU */
            lcp->his_mru = wantmtu;
            nak.hdr.id = TY_MRU;
            nak.hdr.len = 4;
            ua_htons(&lcp->his_mru, nak.data);
            fsm_nak(dec, &nak);
          } else {
            lcp->his_mru = mru;
            fsm_ack(dec, opt);
          }
          break;
        case MODE_NAK:
          maxmru = p ? physical_DeviceMTU(p) : 0;
          if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
            maxmru = lcp->cfg.max_mru;
          wantmru = lcp->cfg.mru > maxmru ? maxmru : lcp->cfg.mru;
  
          if (wantmru && mru > wantmru)
            lcp->want_mru = wantmru;
          else if (mru > maxmru)
            lcp->want_mru = maxmru;
          else if (mru < MIN_MRU)
            lcp->want_mru = MIN_MRU;
          else
            lcp->want_mru = mru;
          break;
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
  	/* Set the MRU to what we want anyway - the peer won't care! */
  	if (lcp->his_mru > lcp->want_mru)
  	  lcp->his_mru = lcp->want_mru;
          break;
        }
        break;
  
      case TY_ACCMAP:
        ua_ntohl(opt->data, &accmap);
        log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)accmap);
  
        switch (mode_type) {
        case MODE_REQ:
          lcp->his_accmap = accmap;
          fsm_ack(dec, opt);
          break;
        case MODE_NAK:
          lcp->want_accmap = accmap;
          break;
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_AUTHPROTO:
        ua_ntohs(opt->data, &proto);
        chap_type = opt->hdr.len == 5 ? opt->data[2] : 0;
  
        log_Printf(LogLCP, "%s 0x%04x (%s)\n", request, proto,
                   Auth2Nam(proto, chap_type));
  
        switch (mode_type) {
        case MODE_REQ:
          switch (proto) {
          case PROTO_PAP:
            if (opt->hdr.len == 4 && IsAccepted(lcp->cfg.pap)) {
              lcp->his_auth = proto;
              lcp->his_authtype = 0;
              fsm_ack(dec, opt);
            } else if (!lcp_auth_nak(lcp, dec)) {
              lcp->my_reject |= (1 << opt->hdr.id);
              fsm_rej(dec, opt);
            }
            break;
  
          case PROTO_CHAP:
            if ((chap_type == 0x05 && IsAccepted(lcp->cfg.chap05))
  #ifndef NODES
                || (chap_type == 0x80 && (IsAccepted(lcp->cfg.chap80nt) ||
                                     (IsAccepted(lcp->cfg.chap80lm))))
                || (chap_type == 0x81 && IsAccepted(lcp->cfg.chap81))
  #endif
               ) {
              lcp->his_auth = proto;
              lcp->his_authtype = chap_type;
              fsm_ack(dec, opt);
            } else {
  #ifdef NODES
              if (chap_type == 0x80) {
                log_Printf(LogWARN, "CHAP 0x80 not available without DES\n");
              } else if (chap_type == 0x81) {
                log_Printf(LogWARN, "CHAP 0x81 not available without DES\n");
              } else
  #endif
              if (chap_type != 0x05)
                log_Printf(LogWARN, "%s not supported\n",
                           Auth2Nam(PROTO_CHAP, chap_type));
  
              if (!lcp_auth_nak(lcp, dec)) {
                lcp->my_reject |= (1 << opt->hdr.id);
                fsm_rej(dec, opt);
              }
            }
            break;
  
          default:
            log_Printf(LogLCP, "%s 0x%04x - not recognised\n",
                      request, proto);
            if (!lcp_auth_nak(lcp, dec)) {
              lcp->my_reject |= (1 << opt->hdr.id);
              fsm_rej(dec, opt);
            }
            break;
          }
          break;
  
        case MODE_NAK:
          switch (proto) {
          case PROTO_PAP:
            if (IsEnabled(lcp->cfg.pap)) {
              lcp->want_auth = PROTO_PAP;
              lcp->want_authtype = 0;
            } else {
              log_Printf(LogLCP, "Peer will only send PAP (not enabled)\n");
              lcp->his_reject |= (1 << opt->hdr.id);
            }
            break;
          case PROTO_CHAP:
            if (chap_type == 0x05 && IsEnabled(lcp->cfg.chap05)) {
              lcp->want_auth = PROTO_CHAP;
              lcp->want_authtype = 0x05;
  #ifndef NODES
            } else if (chap_type == 0x80 && (IsEnabled(lcp->cfg.chap80nt) ||
                                             IsEnabled(lcp->cfg.chap80lm))) {
              lcp->want_auth = PROTO_CHAP;
              lcp->want_authtype = 0x80;
            } else if (chap_type == 0x81 && IsEnabled(lcp->cfg.chap81)) {
              lcp->want_auth = PROTO_CHAP;
              lcp->want_authtype = 0x81;
  #endif
            } else {
  #ifdef NODES
              if (chap_type == 0x80) {
                log_Printf(LogLCP, "Peer will only send MSCHAP (not available"
                           " without DES)\n");
              } else if (chap_type == 0x81) {
                log_Printf(LogLCP, "Peer will only send MSCHAPV2 (not available"
                           " without DES)\n");
              } else
  #endif
              log_Printf(LogLCP, "Peer will only send %s (not %s)\n",
                         Auth2Nam(PROTO_CHAP, chap_type),
  #ifndef NODES
                         (chap_type == 0x80 || chap_type == 0x81) ? "configured" :
  #endif
                         "supported");
              lcp->his_reject |= (1 << opt->hdr.id);
            }
            break;
          default:
            /* We've been NAK'd with something we don't understand :-( */
            lcp->his_reject |= (1 << opt->hdr.id);
            break;
          }
          break;
  
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_QUALPROTO:
        req = &reqbuff;
        memcpy(req, opt, sizeof(reqbuff));
        log_Printf(LogLCP, "%s proto %x, interval %lums\n",
                  request, ntohs(req->proto), (u_long)ntohl(req->period) * 10);
        switch (mode_type) {
        case MODE_REQ:
          if (ntohs(req->proto) != PROTO_LQR || !IsAccepted(lcp->cfg.lqr)) {
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          } else {
            lcp->his_lqrperiod = ntohl(req->period);
            if (lcp->his_lqrperiod < MIN_LQRPERIOD * 100)
              lcp->his_lqrperiod = MIN_LQRPERIOD * 100;
            req->period = htonl(lcp->his_lqrperiod);
            fsm_ack(dec, opt);
          }
          break;
        case MODE_NAK:
          lcp->want_lqrperiod = ntohl(req->period);
          break;
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_MAGICNUM:
        ua_ntohl(opt->data, &magic);
        log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)magic);
  
        switch (mode_type) {
        case MODE_REQ:
          if (lcp->want_magic) {
            /* Validate magic number */
            if (magic == lcp->want_magic) {
              sigset_t emptyset;
  
              log_Printf(LogLCP, "Magic is same (%08lx) - %d times\n",
                        (u_long)magic, ++lcp->LcpFailedMagic);
              lcp->want_magic = GenerateMagic();
              fsm_nak(dec, opt);
              ualarm(TICKUNIT * (4 + 4 * lcp->LcpFailedMagic), 0);
              sigemptyset(&emptyset);
              sigsuspend(&emptyset);
            } else {
              lcp->his_magic = magic;
              lcp->LcpFailedMagic = 0;
              fsm_ack(dec, opt);
            }
          } else {
            lcp->my_reject |= (1 << opt->hdr.id);
            fsm_rej(dec, opt);
          }
          break;
        case MODE_NAK:
          log_Printf(LogLCP, " Magic 0x%08lx is NAKed!\n", (u_long)magic);
          lcp->want_magic = GenerateMagic();
          break;
        case MODE_REJ:
          log_Printf(LogLCP, " Magic 0x%08x is REJected!\n", magic);
          lcp->want_magic = 0;
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_PROTOCOMP:
        log_Printf(LogLCP, "%s\n", request);
  
        switch (mode_type) {
        case MODE_REQ:
          if (IsAccepted(lcp->cfg.protocomp)) {
            lcp->his_protocomp = 1;
            fsm_ack(dec, opt);
          } else {
  #ifdef OLDMST
            /* MorningStar before v1.3 needs NAK */
            fsm_nak(dec, opt);
  #else
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
  #endif
          }
          break;
        case MODE_NAK:
        case MODE_REJ:
          lcp->want_protocomp = 0;
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_ACFCOMP:
        log_Printf(LogLCP, "%s\n", request);
        switch (mode_type) {
        case MODE_REQ:
          if (IsAccepted(lcp->cfg.acfcomp)) {
            lcp->his_acfcomp = 1;
            fsm_ack(dec, opt);
          } else {
  #ifdef OLDMST
            /* MorningStar before v1.3 needs NAK */
            fsm_nak(dec, opt);
  #else
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
  #endif
          }
          break;
        case MODE_NAK:
        case MODE_REJ:
          lcp->want_acfcomp = 0;
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      case TY_SDP:
        log_Printf(LogLCP, "%s\n", request);
        switch (mode_type) {
        case MODE_REQ:
        case MODE_NAK:
        case MODE_REJ:
          break;
        }
        break;
  
      case TY_CALLBACK:
        if (opt->hdr.len == 2)
          op = CALLBACK_NONE;
        else
          op = (int)opt->data[0];
        sz = opt->hdr.len - 3;
        switch (op) {
          case CALLBACK_AUTH:
            log_Printf(LogLCP, "%s Auth\n", request);
            break;
          case CALLBACK_DIALSTRING:
            log_Printf(LogLCP, "%s Dialstring %.*s\n", request, sz,
                       opt->data + 1);
            break;
          case CALLBACK_LOCATION:
            log_Printf(LogLCP, "%s Location %.*s\n", request, sz, opt->data + 1);
            break;
          case CALLBACK_E164:
            log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, sz, opt->data + 1);
            break;
          case CALLBACK_NAME:
            log_Printf(LogLCP, "%s Name %.*s\n", request, sz, opt->data + 1);
            break;
          case CALLBACK_CBCP:
            log_Printf(LogLCP, "%s CBCP\n", request);
            break;
          default:
            log_Printf(LogLCP, "%s ???\n", request);
            break;
        }
  
        switch (mode_type) {
        case MODE_REQ:
          callback_req = 1;
          if (p->type != PHYS_DIRECT) {
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          }
          nak.hdr.id = opt->hdr.id;
          nak.hdr.len = 3;
          if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(op)) &&
              (op != CALLBACK_AUTH || p->link.lcp.want_auth) &&
              (op != CALLBACK_E164 ||
               E164ok(&p->dl->cfg.callback, opt->data + 1, sz))) {
            lcp->his_callback.opmask = CALLBACK_BIT(op);
            if (sz > sizeof lcp->his_callback.msg - 1) {
              sz = sizeof lcp->his_callback.msg - 1;
              log_Printf(LogWARN, "Truncating option arg to %d octets\n", sz);
            }
            memcpy(lcp->his_callback.msg, opt->data + 1, sz);
            lcp->his_callback.msg[sz] = '\0';
            fsm_ack(dec, opt);
          } else if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
                      p->link.lcp.auth_ineed) {
            nak.data[0] = CALLBACK_AUTH;
            fsm_nak(dec, &nak);
          } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
            nak.data[0] = CALLBACK_CBCP;
            fsm_nak(dec, &nak);
          } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
            nak.data[0] = CALLBACK_E164;
            fsm_nak(dec, &nak);
          } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
            log_Printf(LogWARN, "Cannot insist on auth callback without"
                       " PAP or CHAP enabled !\n");
            nak.data[0] = 2;
            fsm_nak(dec, &nak);
          } else {
            lcp->my_reject |= (1 << opt->hdr.id);
            fsm_rej(dec, opt);
          }
          break;
        case MODE_NAK:
          /* We don't do what he NAKs with, we do things in our preferred order */
          if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH))
            lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_AUTH);
          else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
            lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_CBCP);
          else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164))
            lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_E164);
          if (lcp->want_callback.opmask == CALLBACK_BIT(CALLBACK_NONE)) {
            log_Printf(LogPHASE, "Peer NAKd all callbacks, trying none\n");
            lcp->want_callback.opmask = 0;
          } else if (!lcp->want_callback.opmask) {
            log_Printf(LogPHASE, "Peer NAKd last configured callback\n");
            fsm_Close(&lcp->fsm);
          }
          break;
        case MODE_REJ:
          if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
            lcp->his_reject |= (1 << opt->hdr.id);
            lcp->want_callback.opmask = 0;
          } else {
            log_Printf(LogPHASE, "Peer rejected *required* callback\n");
            fsm_Close(&lcp->fsm);
          }
          break;
        }
        break;
  
      case TY_SHORTSEQ:
        mp = &lcp->fsm.bundle->ncp.mp;
        log_Printf(LogLCP, "%s\n", request);
  
        switch (mode_type) {
        case MODE_REQ:
          if (lcp->want_mrru && IsAccepted(mp->cfg.shortseq)) {
            lcp->his_shortseq = 1;
            fsm_ack(dec, opt);
          } else {
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          }
          break;
        case MODE_NAK:
          /*
           * He's trying to get us to ask for short sequence numbers.
           * We ignore the NAK and honour our configuration file instead.
           */
          break;
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          lcp->want_shortseq = 0;		/* For when we hit MP */
          break;
        }
        break;
  
      case TY_ENDDISC:
        mp = &lcp->fsm.bundle->ncp.mp;
        log_Printf(LogLCP, "%s %s\n", request,
                   mp_Enddisc(opt->data[0], opt->data + 1, opt->hdr.len - 3));
        switch (mode_type) {
        case MODE_REQ:
          if (!p) {
            log_Printf(LogLCP, " ENDDISC rejected - not a physical link\n");
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          } else if (!IsAccepted(mp->cfg.negenddisc)) {
            lcp->my_reject |= (1 << opt->hdr.id);
            fsm_rej(dec, opt);
          } else if (opt->hdr.len - 3 < sizeof p->dl->peer.enddisc.address &&
                     opt->data[0] <= MAX_ENDDISC_CLASS) {
            p->dl->peer.enddisc.class = opt->data[0];
            p->dl->peer.enddisc.len = opt->hdr.len - 3;
            memcpy(p->dl->peer.enddisc.address, opt->data + 1, opt->hdr.len - 3);
            p->dl->peer.enddisc.address[opt->hdr.len - 3] = '\0';
            /* XXX: If mp->active, compare and NAK with mp->peer ? */
            fsm_ack(dec, opt);
          } else {
            if (opt->data[0] > MAX_ENDDISC_CLASS)
              log_Printf(LogLCP, " ENDDISC rejected - unrecognised class %d\n",
                        opt->data[0]);
            else
              log_Printf(LogLCP, " ENDDISC rejected - local max length is %ld\n",
                        (long)(sizeof p->dl->peer.enddisc.address - 1));
            fsm_rej(dec, opt);
            lcp->my_reject |= (1 << opt->hdr.id);
          }
          break;
  
        case MODE_NAK:	/* Treat this as a REJ, we don't vary our disc (yet) */
        case MODE_REJ:
          lcp->his_reject |= (1 << opt->hdr.id);
          break;
        }
        break;
  
      default:
        sz = (sizeof desc - 2) / 2;
        if (sz > opt->hdr.len - 2)
          sz = opt->hdr.len - 2;
        pos = 0;
        desc[0] = sz ? ' ' : '\0';
        for (pos = 0; sz--; pos++)
          snprintf(desc+(pos<<1)+1, sizeof desc - ((pos<<1)+1),
  	  "%02x", opt->data[pos]);
  
        log_Printf(LogLCP, "%s%s\n", request, desc);
  
        if (mode_type == MODE_REQ) {
          fsm_rej(dec, opt);
          lcp->my_reject |= (1 << opt->hdr.id);
        }
        break;
      }
    }
  
    if (mode_type != MODE_NOP) {
      if (mode_type == MODE_REQ && p && p->type == PHYS_DIRECT &&
          p->dl->cfg.callback.opmask && !callback_req &&
          !(p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))) {
        /* We *REQUIRE* that the peer requests callback */
        nak.hdr.id = TY_CALLBACK;
        nak.hdr.len = 3;
        if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
            p->link.lcp.want_auth)
          nak.data[0] = CALLBACK_AUTH;
        else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
          nak.data[0] = CALLBACK_CBCP;
        else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164))
          nak.data[0] = CALLBACK_E164;
        else {
          log_Printf(LogWARN, "Cannot insist on auth callback without"
                     " PAP or CHAP enabled !\n");
          nak.hdr.len = 2;	/* XXX: Silly ! */
        }
        fsm_nak(dec, &nak);
      }
      if (mode_type == MODE_REQ && !lcp->mru_req) {
        mru = DEF_MRU;
        phmtu = p ? physical_DeviceMTU(p) : 0;
        if (phmtu && mru > phmtu)
          mru = phmtu;
        if (mru > lcp->cfg.max_mtu)
          mru = lcp->cfg.max_mtu;
        if (mru < DEF_MRU) {
          /* Don't let the peer use the default MRU */
          lcp->his_mru = lcp->cfg.mtu && lcp->cfg.mtu < mru ? lcp->cfg.mtu : mru;
          nak.hdr.id = TY_MRU;
          nak.hdr.len = 4;
          ua_htons(&lcp->his_mru, nak.data);
          fsm_nak(dec, &nak);
          lcp->mru_req = 1;	/* Don't keep NAK'ing this */
        }
      }
      fsm_opt_normalise(dec);
    }
  }
  
  extern struct mbuf *
  lcp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
  {
    /* Got PROTO_LCP from link */
    m_settype(bp, MB_LCPIN);
    fsm_Input(&l->lcp.fsm, bp);
    return NULL;
  }
  