datalink.c revision 53070
136285Sbrian/*-
236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
336285Sbrian * All rights reserved.
436285Sbrian *
536285Sbrian * Redistribution and use in source and binary forms, with or without
636285Sbrian * modification, are permitted provided that the following conditions
736285Sbrian * are met:
836285Sbrian * 1. Redistributions of source code must retain the above copyright
936285Sbrian *    notice, this list of conditions and the following disclaimer.
1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1136285Sbrian *    notice, this list of conditions and the following disclaimer in the
1236285Sbrian *    documentation and/or other materials provided with the distribution.
1336285Sbrian *
1436285Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1536285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1736285Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1836285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1936285Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2036285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2236285Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2336285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2436285Sbrian * SUCH DAMAGE.
2536285Sbrian *
2650479Speter * $FreeBSD: head/usr.sbin/ppp/datalink.c 53070 1999-11-09 23:21:47Z brian $
2736285Sbrian */
2836285Sbrian
2943313Sbrian#include <sys/param.h>
3036285Sbrian#include <netinet/in.h>
3136285Sbrian#include <netinet/in_systm.h>
3236285Sbrian#include <netinet/ip.h>
3336285Sbrian#include <sys/un.h>
3436285Sbrian
3536285Sbrian#include <ctype.h>
3636285Sbrian#include <stdio.h>
3736285Sbrian#include <stdlib.h>
3836285Sbrian#include <string.h>
3936285Sbrian#include <sys/uio.h>
4036285Sbrian#include <termios.h>
4136285Sbrian
4246686Sbrian#include "layer.h"
4336285Sbrian#include "mbuf.h"
4436285Sbrian#include "log.h"
4536285Sbrian#include "defs.h"
4636285Sbrian#include "timer.h"
4736285Sbrian#include "fsm.h"
4836285Sbrian#include "lcp.h"
4936285Sbrian#include "descriptor.h"
5036285Sbrian#include "lqr.h"
5136285Sbrian#include "hdlc.h"
5236285Sbrian#include "async.h"
5336285Sbrian#include "throughput.h"
5436285Sbrian#include "ccp.h"
5536285Sbrian#include "link.h"
5636285Sbrian#include "physical.h"
5736285Sbrian#include "iplist.h"
5836285Sbrian#include "slcompress.h"
5936285Sbrian#include "ipcp.h"
6036285Sbrian#include "filter.h"
6136285Sbrian#include "mp.h"
6243313Sbrian#ifndef NORADIUS
6343313Sbrian#include "radius.h"
6443313Sbrian#endif
6536285Sbrian#include "bundle.h"
6636285Sbrian#include "chat.h"
6736285Sbrian#include "auth.h"
6836285Sbrian#include "prompt.h"
6946686Sbrian#include "proto.h"
7036285Sbrian#include "pap.h"
7136285Sbrian#include "chap.h"
7236285Sbrian#include "command.h"
7338174Sbrian#include "cbcp.h"
7436285Sbrian#include "datalink.h"
7536285Sbrian
7636285Sbrianstatic void datalink_LoginDone(struct datalink *);
7736285Sbrianstatic void datalink_NewState(struct datalink *, int);
7836285Sbrian
7936285Sbrianstatic void
8036285Sbriandatalink_OpenTimeout(void *v)
8136285Sbrian{
8236285Sbrian  struct datalink *dl = (struct datalink *)v;
8336285Sbrian
8444468Sbrian  timer_Stop(&dl->dial.timer);
8536285Sbrian  if (dl->state == DATALINK_OPENING)
8636285Sbrian    log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
8736285Sbrian}
8836285Sbrian
8944261Sbrianstatic int
9036285Sbriandatalink_StartDialTimer(struct datalink *dl, int Timeout)
9136285Sbrian{
9244261Sbrian  int result = Timeout;
9344261Sbrian
9444468Sbrian  timer_Stop(&dl->dial.timer);
9538174Sbrian  if (Timeout) {
9636285Sbrian    if (Timeout > 0)
9744468Sbrian      dl->dial.timer.load = Timeout * SECTICKS;
9844261Sbrian    else {
9944261Sbrian      result = (random() % DIAL_TIMEOUT) + 1;
10044468Sbrian      dl->dial.timer.load = result * SECTICKS;
10144261Sbrian    }
10244468Sbrian    dl->dial.timer.func = datalink_OpenTimeout;
10344468Sbrian    dl->dial.timer.name = "dial";
10444468Sbrian    dl->dial.timer.arg = dl;
10544468Sbrian    timer_Start(&dl->dial.timer);
10636285Sbrian    if (dl->state == DATALINK_OPENING)
10736285Sbrian      log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
10836285Sbrian                dl->name, Timeout);
10936285Sbrian  }
11044261Sbrian  return result;
11136285Sbrian}
11236285Sbrian
11336285Sbrianstatic void
11436285Sbriandatalink_HangupDone(struct datalink *dl)
11536285Sbrian{
11636285Sbrian  if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
11746686Sbrian      dl->physical->fd != -1) {
11846686Sbrian    /* Don't close our device if the link is dedicated */
11936285Sbrian    datalink_LoginDone(dl);
12036285Sbrian    return;
12136285Sbrian  }
12236285Sbrian
12352488Sbrian  chat_Destroy(&dl->chat);
12446686Sbrian  physical_Close(dl->physical);
12536285Sbrian  dl->phone.chosen = "N/A";
12636285Sbrian
12738174Sbrian  if (dl->cbcp.required) {
12838174Sbrian    log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
12938174Sbrian    dl->cfg.callback.opmask = 0;
13038174Sbrian    strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
13138174Sbrian            sizeof dl->cfg.phone.list - 1);
13238174Sbrian    dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
13338174Sbrian    dl->phone.alt = dl->phone.next = NULL;
13438174Sbrian    dl->reconnect_tries = dl->cfg.reconnect.max;
13544468Sbrian    dl->dial.tries = dl->cfg.dial.max;
13644468Sbrian    dl->dial.incs = 0;
13738174Sbrian    dl->script.run = 1;
13838174Sbrian    dl->script.packetmode = 1;
13938174Sbrian    if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
14038174Sbrian      log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
14138174Sbrian    bundle_LinksRemoved(dl->bundle);
14244468Sbrian    /* if dial.timeout is < 0 (random), we don't override fsm.delay */
14338174Sbrian    if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
14438174Sbrian      dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
14538174Sbrian    datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
14638174Sbrian    cbcp_Down(&dl->cbcp);
14738174Sbrian    datalink_NewState(dl, DATALINK_OPENING);
14852412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
14952412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
15045385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
15138174Sbrian  } else if (dl->bundle->CleaningUp ||
15236285Sbrian      (dl->physical->type == PHYS_DIRECT) ||
15344468Sbrian      ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15436465Sbrian       !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
15536285Sbrian    datalink_NewState(dl, DATALINK_CLOSED);
15644468Sbrian    dl->dial.tries = -1;
15744468Sbrian    dl->dial.incs = 0;
15836285Sbrian    dl->reconnect_tries = 0;
15936285Sbrian    bundle_LinkClosed(dl->bundle, dl);
16036285Sbrian    if (!dl->bundle->CleaningUp)
16144468Sbrian      datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
16236285Sbrian  } else {
16336285Sbrian    datalink_NewState(dl, DATALINK_OPENING);
16452412Sbrian    if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
16552412Sbrian        bundle_Phase(dl->bundle) == PHASE_TERMINATE)
16645385Sbrian      bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
16744468Sbrian    if (dl->dial.tries < 0) {
16836285Sbrian      datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
16944468Sbrian      dl->dial.tries = dl->cfg.dial.max;
17044468Sbrian      dl->dial.incs = 0;
17136285Sbrian      dl->reconnect_tries--;
17236285Sbrian    } else {
17336285Sbrian      if (dl->phone.next == NULL)
17444468Sbrian        datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
17536285Sbrian      else
17636285Sbrian        datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
17736285Sbrian    }
17836285Sbrian  }
17936285Sbrian}
18036285Sbrian
18149472Sbrianconst char *
18236285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl)
18336285Sbrian{
18436285Sbrian  char *phone;
18536285Sbrian
18636285Sbrian  if (dl->phone.alt == NULL) {
18736285Sbrian    if (dl->phone.next == NULL) {
18836285Sbrian      strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
18936285Sbrian      dl->phone.list[sizeof dl->phone.list - 1] = '\0';
19048003Sbrian      if (*dl->phone.list == '\0')
19148003Sbrian        return "";
19236285Sbrian      dl->phone.next = dl->phone.list;
19336285Sbrian    }
19436285Sbrian    dl->phone.alt = strsep(&dl->phone.next, ":");
19536285Sbrian  }
19636285Sbrian  phone = strsep(&dl->phone.alt, "|");
19736285Sbrian  dl->phone.chosen = *phone ? phone : "[NONE]";
19836285Sbrian  if (*phone)
19936285Sbrian    log_Printf(LogPHASE, "Phone: %s\n", phone);
20036285Sbrian  return phone;
20136285Sbrian}
20236285Sbrian
20336285Sbrianstatic void
20436285Sbriandatalink_LoginDone(struct datalink *dl)
20536285Sbrian{
20652488Sbrian  chat_Destroy(&dl->chat);
20752488Sbrian
20836285Sbrian  if (!dl->script.packetmode) {
20944468Sbrian    dl->dial.tries = -1;
21044468Sbrian    dl->dial.incs = 0;
21136285Sbrian    datalink_NewState(dl, DATALINK_READY);
21246686Sbrian  } else if (!physical_Raw(dl->physical)) {
21344468Sbrian    dl->dial.tries = 0;
21436285Sbrian    log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
21536285Sbrian    if (dl->script.run) {
21652488Sbrian      datalink_NewState(dl, DATALINK_LOGOUT);
21752488Sbrian      chat_Init(&dl->chat, dl->physical, dl->cfg.script.logout, NULL);
21836285Sbrian    } else {
21947061Sbrian      physical_StopDeviceTimer(dl->physical);
22036285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
22136285Sbrian        /* force a redial timeout */
22246686Sbrian        physical_Close(dl->physical);
22336285Sbrian      datalink_HangupDone(dl);
22436285Sbrian    }
22536285Sbrian  } else {
22644468Sbrian    dl->dial.tries = -1;
22744468Sbrian    dl->dial.incs = 0;
22836285Sbrian
22936285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
23036285Sbrian    async_Init(&dl->physical->async);
23136285Sbrian
23236285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
23336285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
23436285Sbrian    ccp_Setup(&dl->physical->link.ccp);
23536285Sbrian
23636285Sbrian    datalink_NewState(dl, DATALINK_LCP);
23736285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
23836285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
23936285Sbrian  }
24036285Sbrian}
24136285Sbrian
24236285Sbrianstatic int
24336285Sbriandatalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
24436285Sbrian                   int *n)
24536285Sbrian{
24636285Sbrian  struct datalink *dl = descriptor2datalink(d);
24736285Sbrian  int result;
24836285Sbrian
24936285Sbrian  result = 0;
25036285Sbrian  switch (dl->state) {
25136285Sbrian    case DATALINK_CLOSED:
25236465Sbrian      if ((dl->physical->type &
25336465Sbrian           (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
25447863Sbrian          !dl->bundle->CleaningUp)
25536285Sbrian        /*
25636465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
25736465Sbrian         * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
25836465Sbrian         * Go to DATALINK_OPENING via datalink_Up() and fall through.
25936285Sbrian         */
26036285Sbrian        datalink_Up(dl, 1, 1);
26136285Sbrian      else
26236285Sbrian        break;
26336285Sbrian      /* fall through */
26436285Sbrian
26536285Sbrian    case DATALINK_OPENING:
26644468Sbrian      if (dl->dial.timer.state != TIMER_RUNNING) {
26744468Sbrian        if (--dl->dial.tries < 0)
26844468Sbrian          dl->dial.tries = 0;
26946686Sbrian        if (physical_Open(dl->physical, dl->bundle) >= 0) {
27038200Sbrian          log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
27138200Sbrian                           "Type `~?' for help\r\n", dl->name,
27238200Sbrian                           dl->physical->name.full);
27336285Sbrian          if (dl->script.run) {
27436285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
27552488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial,
27649472Sbrian                      *dl->cfg.script.dial ?
27749472Sbrian                      datalink_ChoosePhoneNumber(dl) : "");
27836465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
27936285Sbrian                dl->cfg.dial.max)
28036285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
28144468Sbrian                        dl->name, dl->cfg.dial.max - dl->dial.tries,
28236285Sbrian                        dl->cfg.dial.max);
28336285Sbrian          } else
28451978Sbrian            datalink_NewState(dl, DATALINK_CARRIER);
28541830Sbrian          return datalink_UpdateSet(d, r, w, e, n);
28636285Sbrian        } else {
28736465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28836285Sbrian              dl->cfg.dial.max)
28946686Sbrian            log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
29044468Sbrian                       dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
29136285Sbrian          else
29246686Sbrian            log_Printf(LogCHAT, "Failed to open device\n");
29336285Sbrian
29436285Sbrian          if (dl->bundle->CleaningUp ||
29536465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29644468Sbrian               dl->cfg.dial.max && dl->dial.tries == 0)) {
29736285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
29836285Sbrian            dl->reconnect_tries = 0;
29944468Sbrian            dl->dial.tries = -1;
30038200Sbrian            log_WritePrompts(dl, "Failed to open %s\n",
30138200Sbrian                             dl->physical->name.full);
30236285Sbrian            bundle_LinkClosed(dl->bundle, dl);
30336285Sbrian          }
30438200Sbrian          if (!dl->bundle->CleaningUp) {
30544468Sbrian            int timeout;
30644468Sbrian
30744468Sbrian            timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
30838200Sbrian            log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
30944261Sbrian                             dl->physical->name.full, timeout);
31038200Sbrian          }
31136285Sbrian        }
31236285Sbrian      }
31336285Sbrian      break;
31436285Sbrian
31549472Sbrian    case DATALINK_CARRIER:
31649472Sbrian      /* Wait for carrier on the device */
31749472Sbrian      switch (physical_AwaitCarrier(dl->physical)) {
31849472Sbrian        case CARRIER_PENDING:
31949472Sbrian          log_Printf(LogDEBUG, "Waiting for carrier\n");
32049472Sbrian          return 0;	/* A device timer is running to wake us up again */
32149472Sbrian
32249472Sbrian        case CARRIER_OK:
32351978Sbrian          if (dl->script.run) {
32451978Sbrian            datalink_NewState(dl, DATALINK_LOGIN);
32552488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, NULL);
32651978Sbrian          } else
32751978Sbrian            datalink_LoginDone(dl);
32849472Sbrian          return datalink_UpdateSet(d, r, w, e, n);
32949472Sbrian
33049472Sbrian        case CARRIER_LOST:
33149472Sbrian          physical_Offline(dl->physical);	/* Is this required ? */
33251978Sbrian          if (dl->script.run) {
33351978Sbrian            datalink_NewState(dl, DATALINK_HANGUP);
33452488Sbrian            chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
33553070Sbrian            return datalink_UpdateSet(d, r, w, e, n);
33653070Sbrian          } else {
33751978Sbrian            datalink_HangupDone(dl);
33853070Sbrian            return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
33953070Sbrian          }
34049472Sbrian      }
34149472Sbrian
34236285Sbrian    case DATALINK_HANGUP:
34336285Sbrian    case DATALINK_DIAL:
34452488Sbrian    case DATALINK_LOGOUT:
34536285Sbrian    case DATALINK_LOGIN:
34636285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
34736285Sbrian      switch (dl->chat.state) {
34836285Sbrian        case CHAT_DONE:
34936285Sbrian          /* script succeeded */
35036285Sbrian          switch(dl->state) {
35136285Sbrian            case DATALINK_HANGUP:
35236285Sbrian              datalink_HangupDone(dl);
35336285Sbrian              break;
35436285Sbrian            case DATALINK_DIAL:
35549472Sbrian              datalink_NewState(dl, DATALINK_CARRIER);
35636285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
35752488Sbrian            case DATALINK_LOGOUT:
35852488Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
35952488Sbrian              physical_Offline(dl->physical);
36052488Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
36152488Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36236285Sbrian            case DATALINK_LOGIN:
36342390Sbrian              dl->phone.alt = NULL;
36436285Sbrian              datalink_LoginDone(dl);
36542905Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36636285Sbrian          }
36736285Sbrian          break;
36836285Sbrian        case CHAT_FAILED:
36936285Sbrian          /* Going down - script failed */
37036285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
37136285Sbrian          switch(dl->state) {
37236285Sbrian            case DATALINK_HANGUP:
37336285Sbrian              datalink_HangupDone(dl);
37436285Sbrian              break;
37536285Sbrian            case DATALINK_DIAL:
37652488Sbrian            case DATALINK_LOGOUT:
37736285Sbrian            case DATALINK_LOGIN:
37836285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
37952488Sbrian              physical_Offline(dl->physical);
38052488Sbrian              chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
38136285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
38236285Sbrian          }
38336285Sbrian          break;
38436285Sbrian      }
38536285Sbrian      break;
38636285Sbrian
38736285Sbrian    case DATALINK_READY:
38836285Sbrian    case DATALINK_LCP:
38936285Sbrian    case DATALINK_AUTH:
39038174Sbrian    case DATALINK_CBCP:
39136285Sbrian    case DATALINK_OPEN:
39243888Sbrian      result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
39343888Sbrian               descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
39436285Sbrian      break;
39536285Sbrian  }
39636285Sbrian  return result;
39736285Sbrian}
39836285Sbrian
39936285Sbrianint
40036285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
40136285Sbrian{
40236285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
40336285Sbrian}
40436285Sbrian
40536285Sbrianstatic int
40636285Sbriandatalink_IsSet(struct descriptor *d, const fd_set *fdset)
40736285Sbrian{
40836285Sbrian  struct datalink *dl = descriptor2datalink(d);
40936285Sbrian
41036285Sbrian  switch (dl->state) {
41136285Sbrian    case DATALINK_CLOSED:
41236285Sbrian    case DATALINK_OPENING:
41336285Sbrian      break;
41436285Sbrian
41536285Sbrian    case DATALINK_HANGUP:
41636285Sbrian    case DATALINK_DIAL:
41752488Sbrian    case DATALINK_LOGOUT:
41836285Sbrian    case DATALINK_LOGIN:
41936285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
42036285Sbrian
42136285Sbrian    case DATALINK_READY:
42236285Sbrian    case DATALINK_LCP:
42336285Sbrian    case DATALINK_AUTH:
42438174Sbrian    case DATALINK_CBCP:
42536285Sbrian    case DATALINK_OPEN:
42643888Sbrian      return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
42743888Sbrian             descriptor_IsSet(&dl->physical->desc, fdset);
42836285Sbrian  }
42936285Sbrian  return 0;
43036285Sbrian}
43136285Sbrian
43236285Sbrianstatic void
43336285Sbriandatalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
43436285Sbrian{
43536285Sbrian  struct datalink *dl = descriptor2datalink(d);
43636285Sbrian
43736285Sbrian  switch (dl->state) {
43836285Sbrian    case DATALINK_CLOSED:
43936285Sbrian    case DATALINK_OPENING:
44036285Sbrian      break;
44136285Sbrian
44236285Sbrian    case DATALINK_HANGUP:
44336285Sbrian    case DATALINK_DIAL:
44452488Sbrian    case DATALINK_LOGOUT:
44536285Sbrian    case DATALINK_LOGIN:
44636285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
44736285Sbrian      break;
44836285Sbrian
44936285Sbrian    case DATALINK_READY:
45036285Sbrian    case DATALINK_LCP:
45136285Sbrian    case DATALINK_AUTH:
45238174Sbrian    case DATALINK_CBCP:
45336285Sbrian    case DATALINK_OPEN:
45443888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
45543888Sbrian        descriptor_Read(&dl->chap.desc, bundle, fdset);
45643888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
45743888Sbrian        descriptor_Read(&dl->physical->desc, bundle, fdset);
45836285Sbrian      break;
45936285Sbrian  }
46036285Sbrian}
46136285Sbrian
46237141Sbrianstatic int
46336285Sbriandatalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
46436285Sbrian{
46536285Sbrian  struct datalink *dl = descriptor2datalink(d);
46637141Sbrian  int result = 0;
46736285Sbrian
46836285Sbrian  switch (dl->state) {
46936285Sbrian    case DATALINK_CLOSED:
47036285Sbrian    case DATALINK_OPENING:
47136285Sbrian      break;
47236285Sbrian
47336285Sbrian    case DATALINK_HANGUP:
47436285Sbrian    case DATALINK_DIAL:
47552488Sbrian    case DATALINK_LOGOUT:
47636285Sbrian    case DATALINK_LOGIN:
47737141Sbrian      result = descriptor_Write(&dl->chat.desc, bundle, fdset);
47836285Sbrian      break;
47936285Sbrian
48036285Sbrian    case DATALINK_READY:
48136285Sbrian    case DATALINK_LCP:
48236285Sbrian    case DATALINK_AUTH:
48338174Sbrian    case DATALINK_CBCP:
48436285Sbrian    case DATALINK_OPEN:
48543888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
48643888Sbrian        result += descriptor_Write(&dl->chap.desc, bundle, fdset);
48743888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
48843888Sbrian        result += descriptor_Write(&dl->physical->desc, bundle, fdset);
48936285Sbrian      break;
49036285Sbrian  }
49137141Sbrian
49237141Sbrian  return result;
49336285Sbrian}
49436285Sbrian
49536285Sbrianstatic void
49637007Sbriandatalink_ComeDown(struct datalink *dl, int how)
49736285Sbrian{
49837007Sbrian  if (how != CLOSE_NORMAL) {
49944468Sbrian    dl->dial.tries = -1;
50036285Sbrian    dl->reconnect_tries = 0;
50137012Sbrian    if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
50237007Sbrian      dl->stayonline = 1;
50336285Sbrian  }
50436285Sbrian
50537012Sbrian  if (dl->state >= DATALINK_READY && dl->stayonline) {
50637007Sbrian    dl->stayonline = 0;
50747061Sbrian    physical_StopDeviceTimer(dl->physical);
50837007Sbrian    datalink_NewState(dl, DATALINK_READY);
50937007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
51046686Sbrian    physical_Offline(dl->physical);
51136285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
51252488Sbrian      if (dl->state == DATALINK_LOGOUT) {
51352488Sbrian        datalink_NewState(dl, DATALINK_HANGUP);
51452488Sbrian        chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, NULL);
51552488Sbrian      } else {
51652488Sbrian        datalink_NewState(dl, DATALINK_LOGOUT);
51752488Sbrian        chat_Init(&dl->chat, dl->physical, dl->cfg.script.logout, NULL);
51852488Sbrian      }
51936285Sbrian    } else
52036285Sbrian      datalink_HangupDone(dl);
52136285Sbrian  }
52236285Sbrian}
52336285Sbrian
52436285Sbrianstatic void
52536285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
52636285Sbrian{
52736285Sbrian  /* The given FSM is about to start up ! */
52836285Sbrian  struct datalink *dl = (struct datalink *)v;
52936285Sbrian
53036285Sbrian  if (fp->proto == PROTO_LCP)
53136285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
53236285Sbrian}
53336285Sbrian
53436285Sbrianstatic void
53536285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
53636285Sbrian{
53736285Sbrian  /* The given fsm is now up */
53836285Sbrian  struct datalink *dl = (struct datalink *)v;
53944106Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
54036285Sbrian
54136285Sbrian  if (fp->proto == PROTO_LCP) {
54243693Sbrian    datalink_GotAuthname(dl, "");
54344106Sbrian    lcp->auth_ineed = lcp->want_auth;
54444106Sbrian    lcp->auth_iwait = lcp->his_auth;
54544106Sbrian    if (lcp->his_auth || lcp->want_auth) {
54645350Sbrian      if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
54736285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
54836285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
54944106Sbrian                Auth2Nam(lcp->his_auth, lcp->his_authtype),
55044106Sbrian                Auth2Nam(lcp->want_auth, lcp->want_authtype));
55144106Sbrian      if (lcp->his_auth == PROTO_PAP)
55243693Sbrian        auth_StartReq(&dl->pap);
55344106Sbrian      if (lcp->want_auth == PROTO_CHAP)
55443693Sbrian        auth_StartReq(&dl->chap.auth);
55536285Sbrian    } else
55636285Sbrian      datalink_AuthOk(dl);
55736285Sbrian  }
55836285Sbrian}
55936285Sbrian
56044094Sbrianstatic void
56144094Sbriandatalink_AuthReInit(struct datalink *dl)
56244094Sbrian{
56344094Sbrian  auth_StopTimer(&dl->pap);
56444094Sbrian  auth_StopTimer(&dl->chap.auth);
56544094Sbrian  chap_ReInit(&dl->chap);
56644094Sbrian}
56744094Sbrian
56836285Sbrianvoid
56943693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name)
57036285Sbrian{
57143693Sbrian  strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
57243693Sbrian  dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
57336285Sbrian}
57436285Sbrian
57536285Sbrianvoid
57638174Sbriandatalink_NCPUp(struct datalink *dl)
57736285Sbrian{
57837320Sbrian  int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
57936310Sbrian
58036285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
58136285Sbrian    /* we've authenticated in multilink mode ! */
58236285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
58336285Sbrian      case MP_LINKSENT:
58436285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
58536285Sbrian        return;
58636285Sbrian      case MP_UP:
58736285Sbrian        /* First link in the bundle */
58838174Sbrian        auth_Select(dl->bundle, dl->peer.authname);
58949434Sbrian        bundle_CalculateBandwidth(dl->bundle);
59036285Sbrian        /* fall through */
59136285Sbrian      case MP_ADDED:
59236285Sbrian        /* We're in multilink mode ! */
59336310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
59449434Sbrian        bundle_CalculateBandwidth(dl->bundle);
59536285Sbrian        break;
59636285Sbrian      case MP_FAILED:
59736285Sbrian        datalink_AuthNotOk(dl);
59836285Sbrian        return;
59936285Sbrian    }
60036285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
60136285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
60237160Sbrian    datalink_NewState(dl, DATALINK_OPEN);
60349434Sbrian    bundle_CalculateBandwidth(dl->bundle);
60437160Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
60536285Sbrian    return;
60636285Sbrian  } else {
60736285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
60836285Sbrian    ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
60938174Sbrian    auth_Select(dl->bundle, dl->peer.authname);
61036285Sbrian  }
61136285Sbrian
61237320Sbrian  if (ccpok) {
61337320Sbrian    fsm_Up(&dl->physical->link.ccp.fsm);
61437320Sbrian    fsm_Open(&dl->physical->link.ccp.fsm);
61537320Sbrian  }
61636285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
61736285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
61836285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
61936285Sbrian}
62036285Sbrian
62136285Sbrianvoid
62238174Sbriandatalink_CBCPComplete(struct datalink *dl)
62338174Sbrian{
62438174Sbrian  datalink_NewState(dl, DATALINK_LCP);
62544094Sbrian  datalink_AuthReInit(dl);
62638174Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
62738174Sbrian}
62838174Sbrian
62938174Sbrianvoid
63038174Sbriandatalink_CBCPFailed(struct datalink *dl)
63138174Sbrian{
63238174Sbrian  cbcp_Down(&dl->cbcp);
63338174Sbrian  datalink_CBCPComplete(dl);
63438174Sbrian}
63538174Sbrian
63638174Sbrianvoid
63738174Sbriandatalink_AuthOk(struct datalink *dl)
63838174Sbrian{
63942600Sbrian  if ((dl->physical->link.lcp.his_callback.opmask &
64042600Sbrian       CALLBACK_BIT(CALLBACK_CBCP) ||
64142600Sbrian       dl->physical->link.lcp.want_callback.opmask &
64242600Sbrian       CALLBACK_BIT(CALLBACK_CBCP)) &&
64342600Sbrian      !(dl->physical->link.lcp.want_callback.opmask &
64442600Sbrian        CALLBACK_BIT(CALLBACK_AUTH))) {
64542600Sbrian    /* We must have agreed CBCP if AUTH isn't there any more */
64638174Sbrian    datalink_NewState(dl, DATALINK_CBCP);
64738174Sbrian    cbcp_Up(&dl->cbcp);
64838174Sbrian  } else if (dl->physical->link.lcp.want_callback.opmask) {
64942600Sbrian    /* It's not CBCP */
65038174Sbrian    log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
65138174Sbrian    datalink_NewState(dl, DATALINK_LCP);
65244094Sbrian    datalink_AuthReInit(dl);
65338174Sbrian    fsm_Close(&dl->physical->link.lcp.fsm);
65438174Sbrian  } else
65538174Sbrian    switch (dl->physical->link.lcp.his_callback.opmask) {
65638174Sbrian      case 0:
65738174Sbrian        datalink_NCPUp(dl);
65838174Sbrian        break;
65938174Sbrian
66038174Sbrian      case CALLBACK_BIT(CALLBACK_AUTH):
66138174Sbrian        auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
66238174Sbrian                          sizeof dl->cbcp.fsm.phone);
66338174Sbrian        if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
66438174Sbrian          log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
66538174Sbrian                     dl->peer.authname);
66638174Sbrian          *dl->cbcp.fsm.phone = '\0';
66738174Sbrian        } else {
66838174Sbrian          char *ptr = strchr(dl->cbcp.fsm.phone, ',');
66938174Sbrian          if (ptr)
67038174Sbrian            *ptr = '\0';	/* Call back on the first number */
67138174Sbrian          log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
67238174Sbrian                     dl->cbcp.fsm.phone);
67338174Sbrian          dl->cbcp.required = 1;
67438174Sbrian        }
67538174Sbrian        dl->cbcp.fsm.delay = 0;
67638174Sbrian        datalink_NewState(dl, DATALINK_LCP);
67744094Sbrian        datalink_AuthReInit(dl);
67838174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
67938174Sbrian        break;
68038174Sbrian
68138174Sbrian      case CALLBACK_BIT(CALLBACK_E164):
68238174Sbrian        strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
68338174Sbrian                sizeof dl->cbcp.fsm.phone - 1);
68438174Sbrian        dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
68538174Sbrian        log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
68638174Sbrian                   dl->cbcp.fsm.phone);
68738174Sbrian        dl->cbcp.required = 1;
68838174Sbrian        dl->cbcp.fsm.delay = 0;
68938174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69044094Sbrian        datalink_AuthReInit(dl);
69138174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
69238174Sbrian        break;
69338174Sbrian
69438174Sbrian      default:
69538174Sbrian        log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
69638174Sbrian                   dl->name);
69738174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69844094Sbrian        datalink_AuthReInit(dl);
69938174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
70038174Sbrian        break;
70138174Sbrian    }
70238174Sbrian}
70338174Sbrian
70438174Sbrianvoid
70536285Sbriandatalink_AuthNotOk(struct datalink *dl)
70636285Sbrian{
70736285Sbrian  datalink_NewState(dl, DATALINK_LCP);
70844094Sbrian  datalink_AuthReInit(dl);
70936285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
71036285Sbrian}
71136285Sbrian
71236285Sbrianstatic void
71336285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
71436285Sbrian{
71536285Sbrian  /* The given FSM has been told to come down */
71636285Sbrian  struct datalink *dl = (struct datalink *)v;
71736285Sbrian
71836285Sbrian  if (fp->proto == PROTO_LCP) {
71936285Sbrian    switch (dl->state) {
72036285Sbrian      case DATALINK_OPEN:
72136285Sbrian        peerid_Init(&dl->peer);
72237060Sbrian        fsm2initial(&dl->physical->link.ccp.fsm);
72336928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
72436285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
72538174Sbrian        /* fall through (just in case) */
72636285Sbrian
72738174Sbrian      case DATALINK_CBCP:
72838174Sbrian        if (!dl->cbcp.required)
72938174Sbrian          cbcp_Down(&dl->cbcp);
73038174Sbrian        /* fall through (just in case) */
73138174Sbrian
73236285Sbrian      case DATALINK_AUTH:
73336285Sbrian        timer_Stop(&dl->pap.authtimer);
73436285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
73536285Sbrian    }
73636285Sbrian    datalink_NewState(dl, DATALINK_LCP);
73744094Sbrian    datalink_AuthReInit(dl);
73836285Sbrian  }
73936285Sbrian}
74036285Sbrian
74136285Sbrianstatic void
74236285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
74336285Sbrian{
74436285Sbrian  /* The given fsm is now down */
74536285Sbrian  struct datalink *dl = (struct datalink *)v;
74636285Sbrian
74736285Sbrian  if (fp->proto == PROTO_LCP) {
74837060Sbrian    fsm2initial(fp);
74936285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
75037007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
75136285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
75236285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
75336285Sbrian}
75436285Sbrian
75536285Sbrianstruct datalink *
75636285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
75736285Sbrian{
75836285Sbrian  struct datalink *dl;
75936285Sbrian
76036285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
76136285Sbrian  if (dl == NULL)
76236285Sbrian    return dl;
76336285Sbrian
76436285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
76536285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
76636285Sbrian  dl->desc.IsSet = datalink_IsSet;
76736285Sbrian  dl->desc.Read = datalink_Read;
76836285Sbrian  dl->desc.Write = datalink_Write;
76936285Sbrian
77036285Sbrian  dl->state = DATALINK_CLOSED;
77136285Sbrian
77236285Sbrian  *dl->cfg.script.dial = '\0';
77336285Sbrian  *dl->cfg.script.login = '\0';
77452488Sbrian  *dl->cfg.script.logout = '\0';
77536285Sbrian  *dl->cfg.script.hangup = '\0';
77636285Sbrian  *dl->cfg.phone.list = '\0';
77736285Sbrian  *dl->phone.list = '\0';
77836285Sbrian  dl->phone.next = NULL;
77936285Sbrian  dl->phone.alt = NULL;
78036285Sbrian  dl->phone.chosen = "N/A";
78137007Sbrian  dl->stayonline = 0;
78236285Sbrian  dl->script.run = 1;
78336285Sbrian  dl->script.packetmode = 1;
78436285Sbrian  mp_linkInit(&dl->mp);
78536285Sbrian
78636285Sbrian  dl->bundle = bundle;
78736285Sbrian  dl->next = NULL;
78836285Sbrian
78944468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
79036285Sbrian
79144468Sbrian  dl->dial.tries = 0;
79236285Sbrian  dl->cfg.dial.max = 1;
79336285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
79436285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
79544468Sbrian  dl->cfg.dial.inc = 0;
79644468Sbrian  dl->cfg.dial.maxinc = 10;
79736285Sbrian
79836285Sbrian  dl->reconnect_tries = 0;
79936285Sbrian  dl->cfg.reconnect.max = 0;
80036285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
80136285Sbrian
80238174Sbrian  dl->cfg.callback.opmask = 0;
80338174Sbrian  dl->cfg.cbcp.delay = 0;
80438174Sbrian  *dl->cfg.cbcp.phone = '\0';
80538174Sbrian  dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
80638174Sbrian
80736285Sbrian  dl->name = strdup(name);
80836285Sbrian  peerid_Init(&dl->peer);
80936285Sbrian  dl->parent = &bundle->fsm;
81036285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
81136285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
81236285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
81336285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
81436285Sbrian  dl->fsmp.object = dl;
81536285Sbrian
81646686Sbrian  if ((dl->physical = physical_Create(dl, type)) == NULL) {
81736285Sbrian    free(dl->name);
81836285Sbrian    free(dl);
81936285Sbrian    return NULL;
82036285Sbrian  }
82143693Sbrian
82243693Sbrian  pap_Init(&dl->pap, dl->physical);
82343693Sbrian  chap_Init(&dl->chap, dl->physical);
82438174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
82536285Sbrian
82652488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
82752488Sbrian  chat_Init(&dl->chat, dl->physical, NULL, NULL);
82852488Sbrian
82936285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
83036285Sbrian             dl->name, datalink_State(dl));
83136285Sbrian
83236285Sbrian  return dl;
83336285Sbrian}
83436285Sbrian
83536285Sbrianstruct datalink *
83636285Sbriandatalink_Clone(struct datalink *odl, const char *name)
83736285Sbrian{
83836285Sbrian  struct datalink *dl;
83936285Sbrian
84036285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
84136285Sbrian  if (dl == NULL)
84236285Sbrian    return dl;
84336285Sbrian
84436285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
84536285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
84636285Sbrian  dl->desc.IsSet = datalink_IsSet;
84736285Sbrian  dl->desc.Read = datalink_Read;
84836285Sbrian  dl->desc.Write = datalink_Write;
84936285Sbrian
85036285Sbrian  dl->state = DATALINK_CLOSED;
85136285Sbrian
85236285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
85336285Sbrian  mp_linkInit(&dl->mp);
85436285Sbrian  *dl->phone.list = '\0';
85536285Sbrian  dl->phone.next = NULL;
85636285Sbrian  dl->phone.alt = NULL;
85736285Sbrian  dl->phone.chosen = "N/A";
85836285Sbrian  dl->bundle = odl->bundle;
85936285Sbrian  dl->next = NULL;
86044468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
86144468Sbrian  dl->dial.tries = 0;
86236285Sbrian  dl->reconnect_tries = 0;
86336285Sbrian  dl->name = strdup(name);
86436285Sbrian  peerid_Init(&dl->peer);
86536285Sbrian  dl->parent = odl->parent;
86636285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
86736285Sbrian  dl->fsmp.object = dl;
86836285Sbrian
86946686Sbrian  if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
87036285Sbrian    free(dl->name);
87136285Sbrian    free(dl);
87236285Sbrian    return NULL;
87336285Sbrian  }
87443693Sbrian  pap_Init(&dl->pap, dl->physical);
87544305Sbrian  dl->pap.cfg = odl->pap.cfg;
87643693Sbrian
87743693Sbrian  chap_Init(&dl->chap, dl->physical);
87844305Sbrian  dl->chap.auth.cfg = odl->chap.auth.cfg;
87943693Sbrian
88036285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
88136285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
88236285Sbrian         sizeof dl->physical->link.lcp.cfg);
88336285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
88436285Sbrian         sizeof dl->physical->link.ccp.cfg);
88536285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
88636285Sbrian         sizeof dl->physical->async.cfg);
88736285Sbrian
88838174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
88936285Sbrian
89052488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
89152488Sbrian  chat_Init(&dl->chat, dl->physical, NULL, NULL);
89252488Sbrian
89336285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
89436285Sbrian             dl->name, datalink_State(dl));
89536285Sbrian
89636285Sbrian  return dl;
89736285Sbrian}
89836285Sbrian
89936285Sbrianstruct datalink *
90036285Sbriandatalink_Destroy(struct datalink *dl)
90136285Sbrian{
90236285Sbrian  struct datalink *result;
90336285Sbrian
90436285Sbrian  if (dl->state != DATALINK_CLOSED) {
90536285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
90636285Sbrian              datalink_State(dl));
90736285Sbrian    switch (dl->state) {
90836285Sbrian      case DATALINK_HANGUP:
90936285Sbrian      case DATALINK_DIAL:
91036285Sbrian      case DATALINK_LOGIN:
91136285Sbrian        chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
91236285Sbrian        break;
91336285Sbrian    }
91436285Sbrian  }
91536285Sbrian
91644468Sbrian  timer_Stop(&dl->dial.timer);
91736285Sbrian  result = dl->next;
91846686Sbrian  physical_Destroy(dl->physical);
91936285Sbrian  free(dl->name);
92036285Sbrian  free(dl);
92136285Sbrian
92236285Sbrian  return result;
92336285Sbrian}
92436285Sbrian
92536285Sbrianvoid
92636285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
92736285Sbrian{
92836285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
92936285Sbrian    /* Ignore scripts */
93036285Sbrian    runscripts = 0;
93136285Sbrian
93236285Sbrian  switch (dl->state) {
93336285Sbrian    case DATALINK_CLOSED:
93436285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
93536285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
93636285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
93736285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
93836285Sbrian      dl->reconnect_tries =
93936285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
94044468Sbrian      dl->dial.tries = dl->cfg.dial.max;
94136285Sbrian      dl->script.run = runscripts;
94236285Sbrian      dl->script.packetmode = packetmode;
94336285Sbrian      break;
94436285Sbrian
94536285Sbrian    case DATALINK_OPENING:
94636285Sbrian      if (!dl->script.run && runscripts)
94736285Sbrian        dl->script.run = 1;
94836285Sbrian      /* fall through */
94936285Sbrian
95036285Sbrian    case DATALINK_DIAL:
95136285Sbrian    case DATALINK_LOGIN:
95236285Sbrian    case DATALINK_READY:
95336285Sbrian      if (!dl->script.packetmode && packetmode) {
95436285Sbrian        dl->script.packetmode = 1;
95536285Sbrian        if (dl->state == DATALINK_READY)
95636285Sbrian          datalink_LoginDone(dl);
95736285Sbrian      }
95836285Sbrian      break;
95936285Sbrian  }
96036285Sbrian}
96136285Sbrian
96236285Sbrianvoid
96337007Sbriandatalink_Close(struct datalink *dl, int how)
96436285Sbrian{
96536285Sbrian  /* Please close */
96636285Sbrian  switch (dl->state) {
96736285Sbrian    case DATALINK_OPEN:
96836285Sbrian      peerid_Init(&dl->peer);
96937060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
97036285Sbrian      /* fall through */
97136285Sbrian
97238174Sbrian    case DATALINK_CBCP:
97336285Sbrian    case DATALINK_AUTH:
97436285Sbrian    case DATALINK_LCP:
97544094Sbrian      datalink_AuthReInit(dl);
97636285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
97737007Sbrian      if (how != CLOSE_NORMAL) {
97844468Sbrian        dl->dial.tries = -1;
97936285Sbrian        dl->reconnect_tries = 0;
98037007Sbrian        if (how == CLOSE_LCP)
98137007Sbrian          dl->stayonline = 1;
98236285Sbrian      }
98352029Sbrian      break;
98436285Sbrian
98536285Sbrian    default:
98637007Sbrian      datalink_ComeDown(dl, how);
98736285Sbrian  }
98836285Sbrian}
98936285Sbrian
99036285Sbrianvoid
99137007Sbriandatalink_Down(struct datalink *dl, int how)
99236285Sbrian{
99336285Sbrian  /* Carrier is lost */
99436285Sbrian  switch (dl->state) {
99536285Sbrian    case DATALINK_OPEN:
99636285Sbrian      peerid_Init(&dl->peer);
99737060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
99836285Sbrian      /* fall through */
99936285Sbrian
100038174Sbrian    case DATALINK_CBCP:
100136285Sbrian    case DATALINK_AUTH:
100236285Sbrian    case DATALINK_LCP:
100337060Sbrian      fsm2initial(&dl->physical->link.lcp.fsm);
100436285Sbrian      /* fall through */
100536285Sbrian
100636285Sbrian    default:
100737007Sbrian      datalink_ComeDown(dl, how);
100836285Sbrian  }
100936285Sbrian}
101036285Sbrian
101136285Sbrianvoid
101236285Sbriandatalink_StayDown(struct datalink *dl)
101336285Sbrian{
101436285Sbrian  dl->reconnect_tries = 0;
101536285Sbrian}
101636285Sbrian
101737007Sbrianvoid
101837007Sbriandatalink_DontHangup(struct datalink *dl)
101937007Sbrian{
102037012Sbrian  if (dl->state >= DATALINK_LCP)
102137012Sbrian    dl->stayonline = 1;
102237007Sbrian}
102337007Sbrian
102436285Sbrianint
102536285Sbriandatalink_Show(struct cmdargs const *arg)
102636285Sbrian{
102736285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
102838174Sbrian  prompt_Printf(arg->prompt, " State:              %s\n",
102936285Sbrian                datalink_State(arg->cx));
103038174Sbrian  prompt_Printf(arg->prompt, " Peer name:          ");
103136285Sbrian  if (*arg->cx->peer.authname)
103236285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
103336285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
103436285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
103536285Sbrian  else
103636285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
103738174Sbrian  prompt_Printf(arg->prompt, " Discriminator:      %s\n",
103836285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
103936285Sbrian                           arg->cx->peer.enddisc.address,
104036285Sbrian                           arg->cx->peer.enddisc.len));
104136285Sbrian
104236285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
104338174Sbrian  prompt_Printf(arg->prompt, " Phone List:         %s\n",
104436285Sbrian                arg->cx->cfg.phone.list);
104536285Sbrian  if (arg->cx->cfg.dial.max)
104638174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
104736285Sbrian                  arg->cx->cfg.dial.max);
104836285Sbrian  else
104938174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
105044261Sbrian  if (arg->cx->cfg.dial.next_timeout >= 0)
105136285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
105236285Sbrian  else
105336285Sbrian    prompt_Printf(arg->prompt, "random/");
105444261Sbrian  if (arg->cx->cfg.dial.timeout >= 0)
105536285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
105636285Sbrian  else
105736285Sbrian    prompt_Printf(arg->prompt, "random\n");
105838174Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
105936285Sbrian                arg->cx->cfg.reconnect.max);
106036285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
106136285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
106236285Sbrian  else
106336285Sbrian    prompt_Printf(arg->prompt, "random\n");
106438174Sbrian  prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
106538174Sbrian                PHYS_DIRECT ?  "accepted: " : "requested:");
106638174Sbrian  if (!arg->cx->cfg.callback.opmask)
106738174Sbrian    prompt_Printf(arg->prompt, "none\n");
106838174Sbrian  else {
106938174Sbrian    int comma = 0;
107038174Sbrian
107138174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
107238174Sbrian      prompt_Printf(arg->prompt, "none");
107338174Sbrian      comma = 1;
107438174Sbrian    }
107538174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
107638174Sbrian      prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
107738174Sbrian      comma = 1;
107838174Sbrian    }
107938174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
108038174Sbrian      prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
108138174Sbrian      if (arg->cx->physical->type != PHYS_DIRECT)
108238174Sbrian        prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
108338174Sbrian      comma = 1;
108438174Sbrian    }
108538174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
108638174Sbrian      prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
108738174Sbrian      prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
108838174Sbrian                    arg->cx->cfg.cbcp.delay);
108940483Sbrian      prompt_Printf(arg->prompt, "                     phone: ");
109040483Sbrian      if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
109140483Sbrian        if (arg->cx->physical->type & PHYS_DIRECT)
109240483Sbrian          prompt_Printf(arg->prompt, "Caller decides\n");
109340483Sbrian        else
109440483Sbrian          prompt_Printf(arg->prompt, "Dialback server decides\n");
109540483Sbrian      } else
109640483Sbrian        prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
109738174Sbrian      prompt_Printf(arg->prompt, "                     timeout: %lds\n",
109838174Sbrian                    arg->cx->cfg.cbcp.fsmretry);
109938174Sbrian    } else
110038174Sbrian      prompt_Printf(arg->prompt, "\n");
110138174Sbrian  }
110238174Sbrian
110338174Sbrian  prompt_Printf(arg->prompt, " Dial Script:        %s\n",
110436285Sbrian                arg->cx->cfg.script.dial);
110538174Sbrian  prompt_Printf(arg->prompt, " Login Script:       %s\n",
110636285Sbrian                arg->cx->cfg.script.login);
110752488Sbrian  prompt_Printf(arg->prompt, " Logout Script:      %s\n",
110852488Sbrian                arg->cx->cfg.script.logout);
110938174Sbrian  prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
111036285Sbrian                arg->cx->cfg.script.hangup);
111136285Sbrian  return 0;
111236285Sbrian}
111336285Sbrian
111436285Sbrianint
111536285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
111636285Sbrian{
111736285Sbrian  if (arg->argc == arg->argn+2) {
111836285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
111936285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
112036285Sbrian    return 0;
112136285Sbrian  }
112236285Sbrian  return -1;
112336285Sbrian}
112436285Sbrian
112536285Sbrianint
112636285Sbriandatalink_SetRedial(struct cmdargs const *arg)
112736285Sbrian{
112844468Sbrian  const char *sep, *osep;
112944468Sbrian  int timeout, inc, maxinc, tries;
113036285Sbrian
113136285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
113236285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
113336285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
113436285Sbrian      arg->cx->cfg.dial.timeout = -1;
113536285Sbrian      randinit();
113636285Sbrian    } else {
113736285Sbrian      timeout = atoi(arg->argv[arg->argn]);
113836285Sbrian
113936285Sbrian      if (timeout >= 0)
114036285Sbrian	arg->cx->cfg.dial.timeout = timeout;
114136285Sbrian      else {
114236285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
114336285Sbrian	return -1;
114436285Sbrian      }
114536285Sbrian    }
114636285Sbrian
114744468Sbrian    sep = strchr(arg->argv[arg->argn], '+');
114844468Sbrian    if (sep) {
114944468Sbrian      inc = atoi(++sep);
115044468Sbrian      osep = sep;
115144468Sbrian      if (inc >= 0)
115244468Sbrian        arg->cx->cfg.dial.inc = inc;
115344468Sbrian      else {
115444468Sbrian        log_Printf(LogWARN, "Invalid timeout increment\n");
115544468Sbrian        return -1;
115644468Sbrian      }
115744468Sbrian      sep = strchr(sep, '-');
115844468Sbrian      if (sep) {
115944468Sbrian        maxinc = atoi(++sep);
116044468Sbrian        if (maxinc >= 0)
116144468Sbrian          arg->cx->cfg.dial.maxinc = maxinc;
116244468Sbrian        else {
116344468Sbrian          log_Printf(LogWARN, "Invalid maximum timeout increments\n");
116444468Sbrian          return -1;
116544468Sbrian        }
116644468Sbrian      } else {
116744468Sbrian        /* Default timeout increment */
116844468Sbrian        arg->cx->cfg.dial.maxinc = 10;
116944468Sbrian        sep = osep;
117044468Sbrian      }
117144468Sbrian    } else {
117244468Sbrian      /* Default timeout increment & max increment */
117344468Sbrian      arg->cx->cfg.dial.inc = 0;
117444468Sbrian      arg->cx->cfg.dial.maxinc = 10;
117544468Sbrian      sep = arg->argv[arg->argn];
117644468Sbrian    }
117744468Sbrian
117844468Sbrian    sep = strchr(sep, '.');
117944468Sbrian    if (sep) {
118044468Sbrian      if (strcasecmp(++sep, "random") == 0) {
118136285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
118236285Sbrian	randinit();
118336285Sbrian      } else {
118444468Sbrian	timeout = atoi(sep);
118536285Sbrian	if (timeout >= 0)
118636285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
118736285Sbrian	else {
118836285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
118936285Sbrian	  return -1;
119036285Sbrian	}
119136285Sbrian      }
119236285Sbrian    } else
119336285Sbrian      /* Default next timeout */
119436285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
119536285Sbrian
119636285Sbrian    if (arg->argc == arg->argn+2) {
119736285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
119836285Sbrian
119936285Sbrian      if (tries >= 0) {
120036285Sbrian	arg->cx->cfg.dial.max = tries;
120136285Sbrian      } else {
120236285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
120336285Sbrian	return 1;
120436285Sbrian      }
120536285Sbrian    }
120636285Sbrian    return 0;
120736285Sbrian  }
120844468Sbrian
120936285Sbrian  return -1;
121036285Sbrian}
121136285Sbrian
121236285Sbrianstatic const char *states[] = {
121336285Sbrian  "closed",
121436285Sbrian  "opening",
121536285Sbrian  "hangup",
121636285Sbrian  "dial",
121749472Sbrian  "carrier",
121852488Sbrian  "logout",
121936285Sbrian  "login",
122036285Sbrian  "ready",
122136285Sbrian  "lcp",
122236285Sbrian  "auth",
122338174Sbrian  "cbcp",
122436285Sbrian  "open"
122536285Sbrian};
122636285Sbrian
122736285Sbrianconst char *
122836285Sbriandatalink_State(struct datalink *dl)
122936285Sbrian{
123036285Sbrian  if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
123136285Sbrian    return "unknown";
123236285Sbrian  return states[dl->state];
123336285Sbrian}
123436285Sbrian
123536285Sbrianstatic void
123636285Sbriandatalink_NewState(struct datalink *dl, int state)
123736285Sbrian{
123836285Sbrian  if (state != dl->state) {
123936285Sbrian    if (state >= 0 && state < sizeof states / sizeof states[0]) {
124036285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
124136285Sbrian                 states[state]);
124236285Sbrian      dl->state = state;
124336285Sbrian    } else
124436285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
124536285Sbrian  }
124636285Sbrian}
124736285Sbrian
124836285Sbrianstruct datalink *
124936285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
125052942Sbrian             int fd, int *auxfd, int *nauxfd)
125136285Sbrian{
125236285Sbrian  struct datalink *dl, *cdl;
125344305Sbrian  struct fsm_retry copy;
125436285Sbrian  char *oname;
125536285Sbrian
125636285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
125736285Sbrian  dl->name = iov[*niov].iov_base;
125836285Sbrian
125936285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
126036285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
126136285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
126236285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
126336285Sbrian  }
126436285Sbrian
126536285Sbrian  /* Make sure the name is unique ! */
126636285Sbrian  oname = NULL;
126736285Sbrian  do {
126836285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
126936285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
127036285Sbrian        if (oname)
127136285Sbrian          free(datalink_NextName(dl));
127236285Sbrian        else
127336285Sbrian          oname = datalink_NextName(dl);
127436285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
127536285Sbrian      }
127636285Sbrian  } while (cdl);
127736285Sbrian
127836285Sbrian  if (oname) {
127936285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
128036285Sbrian    free(oname);
128136285Sbrian  } else {
128236285Sbrian    dl->name = strdup(dl->name);
128336285Sbrian    free(iov[*niov].iov_base);
128436285Sbrian  }
128536285Sbrian  (*niov)++;
128636285Sbrian
128736285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
128836285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
128936285Sbrian  dl->desc.IsSet = datalink_IsSet;
129036285Sbrian  dl->desc.Read = datalink_Read;
129136285Sbrian  dl->desc.Write = datalink_Write;
129236285Sbrian
129336285Sbrian  mp_linkInit(&dl->mp);
129436285Sbrian  *dl->phone.list = '\0';
129536285Sbrian  dl->phone.next = NULL;
129636285Sbrian  dl->phone.alt = NULL;
129736285Sbrian  dl->phone.chosen = "N/A";
129836285Sbrian
129936285Sbrian  dl->bundle = bundle;
130036285Sbrian  dl->next = NULL;
130144468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
130244468Sbrian  dl->dial.tries = 0;
130336285Sbrian  dl->reconnect_tries = 0;
130436285Sbrian  dl->parent = &bundle->fsm;
130536285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
130636285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
130736285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
130836285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
130936285Sbrian  dl->fsmp.object = dl;
131036285Sbrian
131152942Sbrian  dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
131236285Sbrian
131336285Sbrian  if (!dl->physical) {
131436285Sbrian    free(dl->name);
131536285Sbrian    free(dl);
131636285Sbrian    dl = NULL;
131736285Sbrian  } else {
131844305Sbrian    copy = dl->pap.cfg.fsm;
131943693Sbrian    pap_Init(&dl->pap, dl->physical);
132044305Sbrian    dl->pap.cfg.fsm = copy;
132143693Sbrian
132244305Sbrian    copy = dl->chap.auth.cfg.fsm;
132343693Sbrian    chap_Init(&dl->chap, dl->physical);
132444305Sbrian    dl->chap.auth.cfg.fsm = copy;
132543693Sbrian
132638174Sbrian    cbcp_Init(&dl->cbcp, dl->physical);
132736285Sbrian
132852488Sbrian    memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
132952488Sbrian    chat_Init(&dl->chat, dl->physical, NULL, NULL);
133052488Sbrian
133136285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
133236285Sbrian              dl->name, datalink_State(dl));
133336285Sbrian  }
133436285Sbrian
133536285Sbrian  return dl;
133636285Sbrian}
133736285Sbrian
133836285Sbrianint
133936450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
134052942Sbrian             int *auxfd, int *nauxfd, pid_t newpid)
134136285Sbrian{
134236285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
134336285Sbrian  int link_fd;
134436285Sbrian
134536285Sbrian  if (dl) {
134644468Sbrian    timer_Stop(&dl->dial.timer);
134738174Sbrian    /* The following is purely for the sake of paranoia */
134838174Sbrian    cbcp_Down(&dl->cbcp);
134936285Sbrian    timer_Stop(&dl->pap.authtimer);
135036285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
135136285Sbrian  }
135236285Sbrian
135336285Sbrian  if (*niov >= maxiov - 1) {
135436285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
135536285Sbrian    if (dl) {
135636285Sbrian      free(dl->name);
135736285Sbrian      free(dl);
135836285Sbrian    }
135936285Sbrian    return -1;
136036285Sbrian  }
136136285Sbrian
136236285Sbrian  iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
136336285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
136436285Sbrian  iov[*niov].iov_base =
136536285Sbrian    dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
136636285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
136736285Sbrian
136852942Sbrian  link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
136952942Sbrian                         nauxfd, newpid);
137036285Sbrian
137136285Sbrian  if (link_fd == -1 && dl) {
137236285Sbrian    free(dl->name);
137336285Sbrian    free(dl);
137436285Sbrian  }
137536285Sbrian
137636285Sbrian  return link_fd;
137736285Sbrian}
137836285Sbrian
137936285Sbrianvoid
138036285Sbriandatalink_Rename(struct datalink *dl, const char *name)
138136285Sbrian{
138236285Sbrian  free(dl->name);
138336285Sbrian  dl->physical->link.name = dl->name = strdup(name);
138436285Sbrian}
138536285Sbrian
138636285Sbrianchar *
138736285Sbriandatalink_NextName(struct datalink *dl)
138836285Sbrian{
138936285Sbrian  int f, n;
139036285Sbrian  char *name, *oname;
139136285Sbrian
139236285Sbrian  n = strlen(dl->name);
139336285Sbrian  name = (char *)malloc(n+3);
139436285Sbrian  for (f = n - 1; f >= 0; f--)
139536285Sbrian    if (!isdigit(dl->name[f]))
139636285Sbrian      break;
139736285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
139836285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
139936285Sbrian  oname = dl->name;
140036345Sbrian  dl->name = name;
140136345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
140236285Sbrian  return oname;
140336285Sbrian}
140436285Sbrian
140536285Sbrianint
140636285Sbriandatalink_SetMode(struct datalink *dl, int mode)
140736285Sbrian{
140836285Sbrian  if (!physical_SetMode(dl->physical, mode))
140936285Sbrian    return 0;
141036285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
141136285Sbrian    dl->script.run = 0;
141236285Sbrian  if (dl->physical->type == PHYS_DIRECT)
141336285Sbrian    dl->reconnect_tries = 0;
141436465Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
141536285Sbrian    datalink_Up(dl, 1, 1);
141636285Sbrian  return 1;
141736285Sbrian}
141844468Sbrian
141944468Sbrianint
142044468Sbriandatalink_GetDialTimeout(struct datalink *dl)
142144468Sbrian{
142244468Sbrian  int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
142344468Sbrian
142444468Sbrian  if (dl->dial.incs < dl->cfg.dial.maxinc)
142544468Sbrian    dl->dial.incs++;
142644468Sbrian
142744468Sbrian  return result;
142844468Sbrian}
1429