datalink.c revision 54914
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 54914 1999-12-20 20:30:02Z 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
12354055Sbrian  chat_Finish(&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{
20654055Sbrian  chat_Finish(&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);
21754914Sbrian      if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
21854914Sbrian        log_Printf(LogWARN, "Invalid logout script\n");
21936285Sbrian    } else {
22047061Sbrian      physical_StopDeviceTimer(dl->physical);
22136285Sbrian      if (dl->physical->type == PHYS_DEDICATED)
22236285Sbrian        /* force a redial timeout */
22346686Sbrian        physical_Close(dl->physical);
22436285Sbrian      datalink_HangupDone(dl);
22536285Sbrian    }
22636285Sbrian  } else {
22744468Sbrian    dl->dial.tries = -1;
22844468Sbrian    dl->dial.incs = 0;
22936285Sbrian
23036285Sbrian    hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
23136285Sbrian    async_Init(&dl->physical->async);
23236285Sbrian
23336285Sbrian    lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
23436285Sbrian              0 : dl->physical->link.lcp.cfg.openmode);
23536285Sbrian    ccp_Setup(&dl->physical->link.ccp);
23636285Sbrian
23736285Sbrian    datalink_NewState(dl, DATALINK_LCP);
23836285Sbrian    fsm_Up(&dl->physical->link.lcp.fsm);
23936285Sbrian    fsm_Open(&dl->physical->link.lcp.fsm);
24036285Sbrian  }
24136285Sbrian}
24236285Sbrian
24336285Sbrianstatic int
24436285Sbriandatalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
24536285Sbrian                   int *n)
24636285Sbrian{
24736285Sbrian  struct datalink *dl = descriptor2datalink(d);
24836285Sbrian  int result;
24936285Sbrian
25036285Sbrian  result = 0;
25136285Sbrian  switch (dl->state) {
25236285Sbrian    case DATALINK_CLOSED:
25353830Sbrian      if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
25453830Sbrian                                 PHYS_FOREGROUND|PHYS_DDIAL)) &&
25547863Sbrian          !dl->bundle->CleaningUp)
25636285Sbrian        /*
25736465Sbrian         * Our first time in - DEDICATED & DDIAL never come down, and
25853830Sbrian         * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
25953830Sbrian         * DATALINK_CLOSED.  Go to DATALINK_OPENING via datalink_Up()
26053830Sbrian         * and fall through.
26136285Sbrian         */
26236285Sbrian        datalink_Up(dl, 1, 1);
26336285Sbrian      else
26436285Sbrian        break;
26536285Sbrian      /* fall through */
26636285Sbrian
26736285Sbrian    case DATALINK_OPENING:
26844468Sbrian      if (dl->dial.timer.state != TIMER_RUNNING) {
26944468Sbrian        if (--dl->dial.tries < 0)
27044468Sbrian          dl->dial.tries = 0;
27146686Sbrian        if (physical_Open(dl->physical, dl->bundle) >= 0) {
27238200Sbrian          log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
27338200Sbrian                           "Type `~?' for help\r\n", dl->name,
27438200Sbrian                           dl->physical->name.full);
27536285Sbrian          if (dl->script.run) {
27636285Sbrian            datalink_NewState(dl, DATALINK_DIAL);
27754914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
27854914Sbrian                            *dl->cfg.script.dial ?
27954914Sbrian                            datalink_ChoosePhoneNumber(dl) : ""))
28054914Sbrian              log_Printf(LogWARN, "Invalid dial script\n");
28136465Sbrian            if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
28236285Sbrian                dl->cfg.dial.max)
28336285Sbrian              log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
28444468Sbrian                        dl->name, dl->cfg.dial.max - dl->dial.tries,
28536285Sbrian                        dl->cfg.dial.max);
28636285Sbrian          } else
28751978Sbrian            datalink_NewState(dl, DATALINK_CARRIER);
28841830Sbrian          return datalink_UpdateSet(d, r, w, e, n);
28936285Sbrian        } else {
29036465Sbrian          if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29136285Sbrian              dl->cfg.dial.max)
29246686Sbrian            log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
29344468Sbrian                       dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
29436285Sbrian          else
29546686Sbrian            log_Printf(LogCHAT, "Failed to open device\n");
29636285Sbrian
29736285Sbrian          if (dl->bundle->CleaningUp ||
29836465Sbrian              (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
29944468Sbrian               dl->cfg.dial.max && dl->dial.tries == 0)) {
30036285Sbrian            datalink_NewState(dl, DATALINK_CLOSED);
30136285Sbrian            dl->reconnect_tries = 0;
30244468Sbrian            dl->dial.tries = -1;
30338200Sbrian            log_WritePrompts(dl, "Failed to open %s\n",
30438200Sbrian                             dl->physical->name.full);
30536285Sbrian            bundle_LinkClosed(dl->bundle, dl);
30636285Sbrian          }
30738200Sbrian          if (!dl->bundle->CleaningUp) {
30844468Sbrian            int timeout;
30944468Sbrian
31044468Sbrian            timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
31138200Sbrian            log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
31244261Sbrian                             dl->physical->name.full, timeout);
31338200Sbrian          }
31436285Sbrian        }
31536285Sbrian      }
31636285Sbrian      break;
31736285Sbrian
31849472Sbrian    case DATALINK_CARRIER:
31949472Sbrian      /* Wait for carrier on the device */
32049472Sbrian      switch (physical_AwaitCarrier(dl->physical)) {
32149472Sbrian        case CARRIER_PENDING:
32249472Sbrian          log_Printf(LogDEBUG, "Waiting for carrier\n");
32349472Sbrian          return 0;	/* A device timer is running to wake us up again */
32449472Sbrian
32549472Sbrian        case CARRIER_OK:
32651978Sbrian          if (dl->script.run) {
32751978Sbrian            datalink_NewState(dl, DATALINK_LOGIN);
32854914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
32954914Sbrian              log_Printf(LogWARN, "Invalid login script\n");
33051978Sbrian          } else
33151978Sbrian            datalink_LoginDone(dl);
33249472Sbrian          return datalink_UpdateSet(d, r, w, e, n);
33349472Sbrian
33449472Sbrian        case CARRIER_LOST:
33549472Sbrian          physical_Offline(dl->physical);	/* Is this required ? */
33651978Sbrian          if (dl->script.run) {
33751978Sbrian            datalink_NewState(dl, DATALINK_HANGUP);
33854914Sbrian            if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
33954914Sbrian              log_Printf(LogWARN, "Invalid hangup script\n");
34053070Sbrian            return datalink_UpdateSet(d, r, w, e, n);
34153070Sbrian          } else {
34251978Sbrian            datalink_HangupDone(dl);
34353070Sbrian            return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
34453070Sbrian          }
34549472Sbrian      }
34649472Sbrian
34736285Sbrian    case DATALINK_HANGUP:
34836285Sbrian    case DATALINK_DIAL:
34952488Sbrian    case DATALINK_LOGOUT:
35036285Sbrian    case DATALINK_LOGIN:
35136285Sbrian      result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
35236285Sbrian      switch (dl->chat.state) {
35336285Sbrian        case CHAT_DONE:
35436285Sbrian          /* script succeeded */
35536285Sbrian          switch(dl->state) {
35636285Sbrian            case DATALINK_HANGUP:
35736285Sbrian              datalink_HangupDone(dl);
35836285Sbrian              break;
35936285Sbrian            case DATALINK_DIAL:
36049472Sbrian              datalink_NewState(dl, DATALINK_CARRIER);
36136285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36252488Sbrian            case DATALINK_LOGOUT:
36352488Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
36452488Sbrian              physical_Offline(dl->physical);
36554914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
36654914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
36752488Sbrian              return datalink_UpdateSet(d, r, w, e, n);
36836285Sbrian            case DATALINK_LOGIN:
36942390Sbrian              dl->phone.alt = NULL;
37036285Sbrian              datalink_LoginDone(dl);
37142905Sbrian              return datalink_UpdateSet(d, r, w, e, n);
37236285Sbrian          }
37336285Sbrian          break;
37436285Sbrian        case CHAT_FAILED:
37536285Sbrian          /* Going down - script failed */
37636285Sbrian          log_Printf(LogWARN, "Chat script failed\n");
37736285Sbrian          switch(dl->state) {
37836285Sbrian            case DATALINK_HANGUP:
37936285Sbrian              datalink_HangupDone(dl);
38036285Sbrian              break;
38136285Sbrian            case DATALINK_DIAL:
38252488Sbrian            case DATALINK_LOGOUT:
38336285Sbrian            case DATALINK_LOGIN:
38436285Sbrian              datalink_NewState(dl, DATALINK_HANGUP);
38552488Sbrian              physical_Offline(dl->physical);
38654914Sbrian              if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
38754914Sbrian                log_Printf(LogWARN, "Invalid hangup script\n");
38836285Sbrian              return datalink_UpdateSet(d, r, w, e, n);
38936285Sbrian          }
39036285Sbrian          break;
39136285Sbrian      }
39236285Sbrian      break;
39336285Sbrian
39436285Sbrian    case DATALINK_READY:
39536285Sbrian    case DATALINK_LCP:
39636285Sbrian    case DATALINK_AUTH:
39738174Sbrian    case DATALINK_CBCP:
39836285Sbrian    case DATALINK_OPEN:
39943888Sbrian      result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
40043888Sbrian               descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
40136285Sbrian      break;
40236285Sbrian  }
40336285Sbrian  return result;
40436285Sbrian}
40536285Sbrian
40636285Sbrianint
40736285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
40836285Sbrian{
40936285Sbrian  return physical_RemoveFromSet(dl->physical, r, w, e);
41036285Sbrian}
41136285Sbrian
41236285Sbrianstatic int
41336285Sbriandatalink_IsSet(struct descriptor *d, const fd_set *fdset)
41436285Sbrian{
41536285Sbrian  struct datalink *dl = descriptor2datalink(d);
41636285Sbrian
41736285Sbrian  switch (dl->state) {
41836285Sbrian    case DATALINK_CLOSED:
41936285Sbrian    case DATALINK_OPENING:
42036285Sbrian      break;
42136285Sbrian
42236285Sbrian    case DATALINK_HANGUP:
42336285Sbrian    case DATALINK_DIAL:
42452488Sbrian    case DATALINK_LOGOUT:
42536285Sbrian    case DATALINK_LOGIN:
42636285Sbrian      return descriptor_IsSet(&dl->chat.desc, fdset);
42736285Sbrian
42836285Sbrian    case DATALINK_READY:
42936285Sbrian    case DATALINK_LCP:
43036285Sbrian    case DATALINK_AUTH:
43138174Sbrian    case DATALINK_CBCP:
43236285Sbrian    case DATALINK_OPEN:
43343888Sbrian      return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
43443888Sbrian             descriptor_IsSet(&dl->physical->desc, fdset);
43536285Sbrian  }
43636285Sbrian  return 0;
43736285Sbrian}
43836285Sbrian
43936285Sbrianstatic void
44036285Sbriandatalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
44136285Sbrian{
44236285Sbrian  struct datalink *dl = descriptor2datalink(d);
44336285Sbrian
44436285Sbrian  switch (dl->state) {
44536285Sbrian    case DATALINK_CLOSED:
44636285Sbrian    case DATALINK_OPENING:
44736285Sbrian      break;
44836285Sbrian
44936285Sbrian    case DATALINK_HANGUP:
45036285Sbrian    case DATALINK_DIAL:
45152488Sbrian    case DATALINK_LOGOUT:
45236285Sbrian    case DATALINK_LOGIN:
45336285Sbrian      descriptor_Read(&dl->chat.desc, bundle, fdset);
45436285Sbrian      break;
45536285Sbrian
45636285Sbrian    case DATALINK_READY:
45736285Sbrian    case DATALINK_LCP:
45836285Sbrian    case DATALINK_AUTH:
45938174Sbrian    case DATALINK_CBCP:
46036285Sbrian    case DATALINK_OPEN:
46143888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
46243888Sbrian        descriptor_Read(&dl->chap.desc, bundle, fdset);
46343888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
46443888Sbrian        descriptor_Read(&dl->physical->desc, bundle, fdset);
46536285Sbrian      break;
46636285Sbrian  }
46736285Sbrian}
46836285Sbrian
46937141Sbrianstatic int
47036285Sbriandatalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
47136285Sbrian{
47236285Sbrian  struct datalink *dl = descriptor2datalink(d);
47337141Sbrian  int result = 0;
47436285Sbrian
47536285Sbrian  switch (dl->state) {
47636285Sbrian    case DATALINK_CLOSED:
47736285Sbrian    case DATALINK_OPENING:
47836285Sbrian      break;
47936285Sbrian
48036285Sbrian    case DATALINK_HANGUP:
48136285Sbrian    case DATALINK_DIAL:
48252488Sbrian    case DATALINK_LOGOUT:
48336285Sbrian    case DATALINK_LOGIN:
48437141Sbrian      result = descriptor_Write(&dl->chat.desc, bundle, fdset);
48536285Sbrian      break;
48636285Sbrian
48736285Sbrian    case DATALINK_READY:
48836285Sbrian    case DATALINK_LCP:
48936285Sbrian    case DATALINK_AUTH:
49038174Sbrian    case DATALINK_CBCP:
49136285Sbrian    case DATALINK_OPEN:
49243888Sbrian      if (descriptor_IsSet(&dl->chap.desc, fdset))
49343888Sbrian        result += descriptor_Write(&dl->chap.desc, bundle, fdset);
49443888Sbrian      if (descriptor_IsSet(&dl->physical->desc, fdset))
49543888Sbrian        result += descriptor_Write(&dl->physical->desc, bundle, fdset);
49636285Sbrian      break;
49736285Sbrian  }
49837141Sbrian
49937141Sbrian  return result;
50036285Sbrian}
50136285Sbrian
50236285Sbrianstatic void
50337007Sbriandatalink_ComeDown(struct datalink *dl, int how)
50436285Sbrian{
50537007Sbrian  if (how != CLOSE_NORMAL) {
50644468Sbrian    dl->dial.tries = -1;
50736285Sbrian    dl->reconnect_tries = 0;
50837012Sbrian    if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
50937007Sbrian      dl->stayonline = 1;
51036285Sbrian  }
51136285Sbrian
51237012Sbrian  if (dl->state >= DATALINK_READY && dl->stayonline) {
51337007Sbrian    dl->stayonline = 0;
51447061Sbrian    physical_StopDeviceTimer(dl->physical);
51537007Sbrian    datalink_NewState(dl, DATALINK_READY);
51637007Sbrian  } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
51746686Sbrian    physical_Offline(dl->physical);
51836285Sbrian    if (dl->script.run && dl->state != DATALINK_OPENING) {
51952488Sbrian      if (dl->state == DATALINK_LOGOUT) {
52052488Sbrian        datalink_NewState(dl, DATALINK_HANGUP);
52154914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
52254914Sbrian          log_Printf(LogWARN, "Invalid hangup script\n");
52352488Sbrian      } else {
52452488Sbrian        datalink_NewState(dl, DATALINK_LOGOUT);
52554914Sbrian        if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
52654914Sbrian          log_Printf(LogWARN, "Invalid logout script\n");
52752488Sbrian      }
52836285Sbrian    } else
52936285Sbrian      datalink_HangupDone(dl);
53036285Sbrian  }
53136285Sbrian}
53236285Sbrian
53336285Sbrianstatic void
53436285Sbriandatalink_LayerStart(void *v, struct fsm *fp)
53536285Sbrian{
53636285Sbrian  /* The given FSM is about to start up ! */
53736285Sbrian  struct datalink *dl = (struct datalink *)v;
53836285Sbrian
53936285Sbrian  if (fp->proto == PROTO_LCP)
54036285Sbrian    (*dl->parent->LayerStart)(dl->parent->object, fp);
54136285Sbrian}
54236285Sbrian
54336285Sbrianstatic void
54436285Sbriandatalink_LayerUp(void *v, struct fsm *fp)
54536285Sbrian{
54636285Sbrian  /* The given fsm is now up */
54736285Sbrian  struct datalink *dl = (struct datalink *)v;
54844106Sbrian  struct lcp *lcp = &dl->physical->link.lcp;
54936285Sbrian
55036285Sbrian  if (fp->proto == PROTO_LCP) {
55143693Sbrian    datalink_GotAuthname(dl, "");
55244106Sbrian    lcp->auth_ineed = lcp->want_auth;
55344106Sbrian    lcp->auth_iwait = lcp->his_auth;
55444106Sbrian    if (lcp->his_auth || lcp->want_auth) {
55545350Sbrian      if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
55636285Sbrian        bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
55736285Sbrian      log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
55844106Sbrian                Auth2Nam(lcp->his_auth, lcp->his_authtype),
55944106Sbrian                Auth2Nam(lcp->want_auth, lcp->want_authtype));
56044106Sbrian      if (lcp->his_auth == PROTO_PAP)
56143693Sbrian        auth_StartReq(&dl->pap);
56244106Sbrian      if (lcp->want_auth == PROTO_CHAP)
56343693Sbrian        auth_StartReq(&dl->chap.auth);
56436285Sbrian    } else
56536285Sbrian      datalink_AuthOk(dl);
56636285Sbrian  }
56736285Sbrian}
56836285Sbrian
56944094Sbrianstatic void
57044094Sbriandatalink_AuthReInit(struct datalink *dl)
57144094Sbrian{
57244094Sbrian  auth_StopTimer(&dl->pap);
57344094Sbrian  auth_StopTimer(&dl->chap.auth);
57444094Sbrian  chap_ReInit(&dl->chap);
57544094Sbrian}
57644094Sbrian
57736285Sbrianvoid
57843693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name)
57936285Sbrian{
58043693Sbrian  strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
58143693Sbrian  dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
58236285Sbrian}
58336285Sbrian
58436285Sbrianvoid
58538174Sbriandatalink_NCPUp(struct datalink *dl)
58636285Sbrian{
58737320Sbrian  int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
58836310Sbrian
58936285Sbrian  if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
59036285Sbrian    /* we've authenticated in multilink mode ! */
59136285Sbrian    switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
59236285Sbrian      case MP_LINKSENT:
59336285Sbrian        /* We've handed the link off to another ppp (well, we will soon) ! */
59436285Sbrian        return;
59536285Sbrian      case MP_UP:
59636285Sbrian        /* First link in the bundle */
59738174Sbrian        auth_Select(dl->bundle, dl->peer.authname);
59849434Sbrian        bundle_CalculateBandwidth(dl->bundle);
59936285Sbrian        /* fall through */
60036285Sbrian      case MP_ADDED:
60136285Sbrian        /* We're in multilink mode ! */
60236310Sbrian        dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
60349434Sbrian        bundle_CalculateBandwidth(dl->bundle);
60436285Sbrian        break;
60536285Sbrian      case MP_FAILED:
60636285Sbrian        datalink_AuthNotOk(dl);
60736285Sbrian        return;
60836285Sbrian    }
60936285Sbrian  } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
61036285Sbrian    log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
61137160Sbrian    datalink_NewState(dl, DATALINK_OPEN);
61249434Sbrian    bundle_CalculateBandwidth(dl->bundle);
61337160Sbrian    (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
61436285Sbrian    return;
61536285Sbrian  } else {
61636285Sbrian    dl->bundle->ncp.mp.peer = dl->peer;
61736285Sbrian    ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
61838174Sbrian    auth_Select(dl->bundle, dl->peer.authname);
61936285Sbrian  }
62036285Sbrian
62137320Sbrian  if (ccpok) {
62237320Sbrian    fsm_Up(&dl->physical->link.ccp.fsm);
62337320Sbrian    fsm_Open(&dl->physical->link.ccp.fsm);
62437320Sbrian  }
62536285Sbrian  datalink_NewState(dl, DATALINK_OPEN);
62636285Sbrian  bundle_NewPhase(dl->bundle, PHASE_NETWORK);
62736285Sbrian  (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
62836285Sbrian}
62936285Sbrian
63036285Sbrianvoid
63138174Sbriandatalink_CBCPComplete(struct datalink *dl)
63238174Sbrian{
63338174Sbrian  datalink_NewState(dl, DATALINK_LCP);
63444094Sbrian  datalink_AuthReInit(dl);
63538174Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
63638174Sbrian}
63738174Sbrian
63838174Sbrianvoid
63938174Sbriandatalink_CBCPFailed(struct datalink *dl)
64038174Sbrian{
64138174Sbrian  cbcp_Down(&dl->cbcp);
64238174Sbrian  datalink_CBCPComplete(dl);
64338174Sbrian}
64438174Sbrian
64538174Sbrianvoid
64638174Sbriandatalink_AuthOk(struct datalink *dl)
64738174Sbrian{
64842600Sbrian  if ((dl->physical->link.lcp.his_callback.opmask &
64942600Sbrian       CALLBACK_BIT(CALLBACK_CBCP) ||
65042600Sbrian       dl->physical->link.lcp.want_callback.opmask &
65142600Sbrian       CALLBACK_BIT(CALLBACK_CBCP)) &&
65242600Sbrian      !(dl->physical->link.lcp.want_callback.opmask &
65342600Sbrian        CALLBACK_BIT(CALLBACK_AUTH))) {
65442600Sbrian    /* We must have agreed CBCP if AUTH isn't there any more */
65538174Sbrian    datalink_NewState(dl, DATALINK_CBCP);
65638174Sbrian    cbcp_Up(&dl->cbcp);
65738174Sbrian  } else if (dl->physical->link.lcp.want_callback.opmask) {
65842600Sbrian    /* It's not CBCP */
65938174Sbrian    log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
66038174Sbrian    datalink_NewState(dl, DATALINK_LCP);
66144094Sbrian    datalink_AuthReInit(dl);
66238174Sbrian    fsm_Close(&dl->physical->link.lcp.fsm);
66338174Sbrian  } else
66438174Sbrian    switch (dl->physical->link.lcp.his_callback.opmask) {
66538174Sbrian      case 0:
66638174Sbrian        datalink_NCPUp(dl);
66738174Sbrian        break;
66838174Sbrian
66938174Sbrian      case CALLBACK_BIT(CALLBACK_AUTH):
67038174Sbrian        auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
67138174Sbrian                          sizeof dl->cbcp.fsm.phone);
67238174Sbrian        if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
67338174Sbrian          log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
67438174Sbrian                     dl->peer.authname);
67538174Sbrian          *dl->cbcp.fsm.phone = '\0';
67638174Sbrian        } else {
67738174Sbrian          char *ptr = strchr(dl->cbcp.fsm.phone, ',');
67838174Sbrian          if (ptr)
67938174Sbrian            *ptr = '\0';	/* Call back on the first number */
68038174Sbrian          log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
68138174Sbrian                     dl->cbcp.fsm.phone);
68238174Sbrian          dl->cbcp.required = 1;
68338174Sbrian        }
68438174Sbrian        dl->cbcp.fsm.delay = 0;
68538174Sbrian        datalink_NewState(dl, DATALINK_LCP);
68644094Sbrian        datalink_AuthReInit(dl);
68738174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
68838174Sbrian        break;
68938174Sbrian
69038174Sbrian      case CALLBACK_BIT(CALLBACK_E164):
69138174Sbrian        strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
69238174Sbrian                sizeof dl->cbcp.fsm.phone - 1);
69338174Sbrian        dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
69438174Sbrian        log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
69538174Sbrian                   dl->cbcp.fsm.phone);
69638174Sbrian        dl->cbcp.required = 1;
69738174Sbrian        dl->cbcp.fsm.delay = 0;
69838174Sbrian        datalink_NewState(dl, DATALINK_LCP);
69944094Sbrian        datalink_AuthReInit(dl);
70038174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
70138174Sbrian        break;
70238174Sbrian
70338174Sbrian      default:
70438174Sbrian        log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
70538174Sbrian                   dl->name);
70638174Sbrian        datalink_NewState(dl, DATALINK_LCP);
70744094Sbrian        datalink_AuthReInit(dl);
70838174Sbrian        fsm_Close(&dl->physical->link.lcp.fsm);
70938174Sbrian        break;
71038174Sbrian    }
71138174Sbrian}
71238174Sbrian
71338174Sbrianvoid
71436285Sbriandatalink_AuthNotOk(struct datalink *dl)
71536285Sbrian{
71636285Sbrian  datalink_NewState(dl, DATALINK_LCP);
71744094Sbrian  datalink_AuthReInit(dl);
71836285Sbrian  fsm_Close(&dl->physical->link.lcp.fsm);
71936285Sbrian}
72036285Sbrian
72136285Sbrianstatic void
72236285Sbriandatalink_LayerDown(void *v, struct fsm *fp)
72336285Sbrian{
72436285Sbrian  /* The given FSM has been told to come down */
72536285Sbrian  struct datalink *dl = (struct datalink *)v;
72636285Sbrian
72736285Sbrian  if (fp->proto == PROTO_LCP) {
72836285Sbrian    switch (dl->state) {
72936285Sbrian      case DATALINK_OPEN:
73036285Sbrian        peerid_Init(&dl->peer);
73137060Sbrian        fsm2initial(&dl->physical->link.ccp.fsm);
73236928Sbrian        datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
73336285Sbrian        (*dl->parent->LayerDown)(dl->parent->object, fp);
73438174Sbrian        /* fall through (just in case) */
73536285Sbrian
73638174Sbrian      case DATALINK_CBCP:
73738174Sbrian        if (!dl->cbcp.required)
73838174Sbrian          cbcp_Down(&dl->cbcp);
73938174Sbrian        /* fall through (just in case) */
74038174Sbrian
74136285Sbrian      case DATALINK_AUTH:
74236285Sbrian        timer_Stop(&dl->pap.authtimer);
74336285Sbrian        timer_Stop(&dl->chap.auth.authtimer);
74436285Sbrian    }
74536285Sbrian    datalink_NewState(dl, DATALINK_LCP);
74644094Sbrian    datalink_AuthReInit(dl);
74736285Sbrian  }
74836285Sbrian}
74936285Sbrian
75036285Sbrianstatic void
75136285Sbriandatalink_LayerFinish(void *v, struct fsm *fp)
75236285Sbrian{
75336285Sbrian  /* The given fsm is now down */
75436285Sbrian  struct datalink *dl = (struct datalink *)v;
75536285Sbrian
75636285Sbrian  if (fp->proto == PROTO_LCP) {
75737060Sbrian    fsm2initial(fp);
75836285Sbrian    (*dl->parent->LayerFinish)(dl->parent->object, fp);
75937007Sbrian    datalink_ComeDown(dl, CLOSE_NORMAL);
76036285Sbrian  } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
76136285Sbrian    fsm_Open(fp);		/* CCP goes to ST_STOPPED */
76236285Sbrian}
76336285Sbrian
76436285Sbrianstruct datalink *
76536285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type)
76636285Sbrian{
76736285Sbrian  struct datalink *dl;
76836285Sbrian
76936285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
77036285Sbrian  if (dl == NULL)
77136285Sbrian    return dl;
77236285Sbrian
77336285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
77436285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
77536285Sbrian  dl->desc.IsSet = datalink_IsSet;
77636285Sbrian  dl->desc.Read = datalink_Read;
77736285Sbrian  dl->desc.Write = datalink_Write;
77836285Sbrian
77936285Sbrian  dl->state = DATALINK_CLOSED;
78036285Sbrian
78136285Sbrian  *dl->cfg.script.dial = '\0';
78236285Sbrian  *dl->cfg.script.login = '\0';
78352488Sbrian  *dl->cfg.script.logout = '\0';
78436285Sbrian  *dl->cfg.script.hangup = '\0';
78536285Sbrian  *dl->cfg.phone.list = '\0';
78636285Sbrian  *dl->phone.list = '\0';
78736285Sbrian  dl->phone.next = NULL;
78836285Sbrian  dl->phone.alt = NULL;
78936285Sbrian  dl->phone.chosen = "N/A";
79037007Sbrian  dl->stayonline = 0;
79136285Sbrian  dl->script.run = 1;
79236285Sbrian  dl->script.packetmode = 1;
79336285Sbrian  mp_linkInit(&dl->mp);
79436285Sbrian
79536285Sbrian  dl->bundle = bundle;
79636285Sbrian  dl->next = NULL;
79736285Sbrian
79844468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
79936285Sbrian
80044468Sbrian  dl->dial.tries = 0;
80136285Sbrian  dl->cfg.dial.max = 1;
80236285Sbrian  dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
80336285Sbrian  dl->cfg.dial.timeout = DIAL_TIMEOUT;
80444468Sbrian  dl->cfg.dial.inc = 0;
80544468Sbrian  dl->cfg.dial.maxinc = 10;
80636285Sbrian
80736285Sbrian  dl->reconnect_tries = 0;
80836285Sbrian  dl->cfg.reconnect.max = 0;
80936285Sbrian  dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
81036285Sbrian
81138174Sbrian  dl->cfg.callback.opmask = 0;
81238174Sbrian  dl->cfg.cbcp.delay = 0;
81338174Sbrian  *dl->cfg.cbcp.phone = '\0';
81438174Sbrian  dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
81538174Sbrian
81636285Sbrian  dl->name = strdup(name);
81736285Sbrian  peerid_Init(&dl->peer);
81836285Sbrian  dl->parent = &bundle->fsm;
81936285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
82036285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
82136285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
82236285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
82336285Sbrian  dl->fsmp.object = dl;
82436285Sbrian
82546686Sbrian  if ((dl->physical = physical_Create(dl, type)) == NULL) {
82636285Sbrian    free(dl->name);
82736285Sbrian    free(dl);
82836285Sbrian    return NULL;
82936285Sbrian  }
83043693Sbrian
83143693Sbrian  pap_Init(&dl->pap, dl->physical);
83243693Sbrian  chap_Init(&dl->chap, dl->physical);
83338174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
83436285Sbrian
83552488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
83654055Sbrian  chat_Init(&dl->chat, dl->physical);
83752488Sbrian
83836285Sbrian  log_Printf(LogPHASE, "%s: Created in %s state\n",
83936285Sbrian             dl->name, datalink_State(dl));
84036285Sbrian
84136285Sbrian  return dl;
84236285Sbrian}
84336285Sbrian
84436285Sbrianstruct datalink *
84536285Sbriandatalink_Clone(struct datalink *odl, const char *name)
84636285Sbrian{
84736285Sbrian  struct datalink *dl;
84836285Sbrian
84936285Sbrian  dl = (struct datalink *)malloc(sizeof(struct datalink));
85036285Sbrian  if (dl == NULL)
85136285Sbrian    return dl;
85236285Sbrian
85336285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
85436285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
85536285Sbrian  dl->desc.IsSet = datalink_IsSet;
85636285Sbrian  dl->desc.Read = datalink_Read;
85736285Sbrian  dl->desc.Write = datalink_Write;
85836285Sbrian
85936285Sbrian  dl->state = DATALINK_CLOSED;
86036285Sbrian
86136285Sbrian  memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
86236285Sbrian  mp_linkInit(&dl->mp);
86336285Sbrian  *dl->phone.list = '\0';
86436285Sbrian  dl->phone.next = NULL;
86536285Sbrian  dl->phone.alt = NULL;
86636285Sbrian  dl->phone.chosen = "N/A";
86736285Sbrian  dl->bundle = odl->bundle;
86836285Sbrian  dl->next = NULL;
86944468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
87044468Sbrian  dl->dial.tries = 0;
87136285Sbrian  dl->reconnect_tries = 0;
87236285Sbrian  dl->name = strdup(name);
87336285Sbrian  peerid_Init(&dl->peer);
87436285Sbrian  dl->parent = odl->parent;
87536285Sbrian  memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
87636285Sbrian  dl->fsmp.object = dl;
87736285Sbrian
87846686Sbrian  if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
87936285Sbrian    free(dl->name);
88036285Sbrian    free(dl);
88136285Sbrian    return NULL;
88236285Sbrian  }
88343693Sbrian  pap_Init(&dl->pap, dl->physical);
88444305Sbrian  dl->pap.cfg = odl->pap.cfg;
88543693Sbrian
88643693Sbrian  chap_Init(&dl->chap, dl->physical);
88744305Sbrian  dl->chap.auth.cfg = odl->chap.auth.cfg;
88843693Sbrian
88936285Sbrian  memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
89036285Sbrian  memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
89136285Sbrian         sizeof dl->physical->link.lcp.cfg);
89236285Sbrian  memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
89336285Sbrian         sizeof dl->physical->link.ccp.cfg);
89436285Sbrian  memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
89536285Sbrian         sizeof dl->physical->async.cfg);
89636285Sbrian
89738174Sbrian  cbcp_Init(&dl->cbcp, dl->physical);
89836285Sbrian
89952488Sbrian  memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
90054055Sbrian  chat_Init(&dl->chat, dl->physical);
90152488Sbrian
90236285Sbrian  log_Printf(LogPHASE, "%s: Cloned in %s state\n",
90336285Sbrian             dl->name, datalink_State(dl));
90436285Sbrian
90536285Sbrian  return dl;
90636285Sbrian}
90736285Sbrian
90836285Sbrianstruct datalink *
90936285Sbriandatalink_Destroy(struct datalink *dl)
91036285Sbrian{
91136285Sbrian  struct datalink *result;
91236285Sbrian
91336285Sbrian  if (dl->state != DATALINK_CLOSED) {
91436285Sbrian    log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
91536285Sbrian              datalink_State(dl));
91636285Sbrian    switch (dl->state) {
91736285Sbrian      case DATALINK_HANGUP:
91836285Sbrian      case DATALINK_DIAL:
91936285Sbrian      case DATALINK_LOGIN:
92054055Sbrian        chat_Finish(&dl->chat);		/* Gotta blat the timers ! */
92136285Sbrian        break;
92236285Sbrian    }
92336285Sbrian  }
92436285Sbrian
92554055Sbrian  chat_Destroy(&dl->chat);
92644468Sbrian  timer_Stop(&dl->dial.timer);
92736285Sbrian  result = dl->next;
92846686Sbrian  physical_Destroy(dl->physical);
92936285Sbrian  free(dl->name);
93036285Sbrian  free(dl);
93136285Sbrian
93236285Sbrian  return result;
93336285Sbrian}
93436285Sbrian
93536285Sbrianvoid
93636285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode)
93736285Sbrian{
93836285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
93936285Sbrian    /* Ignore scripts */
94036285Sbrian    runscripts = 0;
94136285Sbrian
94236285Sbrian  switch (dl->state) {
94336285Sbrian    case DATALINK_CLOSED:
94436285Sbrian      if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
94536285Sbrian          bundle_Phase(dl->bundle) == PHASE_TERMINATE)
94636285Sbrian        bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
94736285Sbrian      datalink_NewState(dl, DATALINK_OPENING);
94836285Sbrian      dl->reconnect_tries =
94936285Sbrian        dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
95044468Sbrian      dl->dial.tries = dl->cfg.dial.max;
95136285Sbrian      dl->script.run = runscripts;
95236285Sbrian      dl->script.packetmode = packetmode;
95336285Sbrian      break;
95436285Sbrian
95536285Sbrian    case DATALINK_OPENING:
95636285Sbrian      if (!dl->script.run && runscripts)
95736285Sbrian        dl->script.run = 1;
95836285Sbrian      /* fall through */
95936285Sbrian
96036285Sbrian    case DATALINK_DIAL:
96136285Sbrian    case DATALINK_LOGIN:
96236285Sbrian    case DATALINK_READY:
96336285Sbrian      if (!dl->script.packetmode && packetmode) {
96436285Sbrian        dl->script.packetmode = 1;
96536285Sbrian        if (dl->state == DATALINK_READY)
96636285Sbrian          datalink_LoginDone(dl);
96736285Sbrian      }
96836285Sbrian      break;
96936285Sbrian  }
97036285Sbrian}
97136285Sbrian
97236285Sbrianvoid
97337007Sbriandatalink_Close(struct datalink *dl, int how)
97436285Sbrian{
97536285Sbrian  /* Please close */
97636285Sbrian  switch (dl->state) {
97736285Sbrian    case DATALINK_OPEN:
97836285Sbrian      peerid_Init(&dl->peer);
97937060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
98036285Sbrian      /* fall through */
98136285Sbrian
98238174Sbrian    case DATALINK_CBCP:
98336285Sbrian    case DATALINK_AUTH:
98436285Sbrian    case DATALINK_LCP:
98544094Sbrian      datalink_AuthReInit(dl);
98636285Sbrian      fsm_Close(&dl->physical->link.lcp.fsm);
98737007Sbrian      if (how != CLOSE_NORMAL) {
98844468Sbrian        dl->dial.tries = -1;
98936285Sbrian        dl->reconnect_tries = 0;
99037007Sbrian        if (how == CLOSE_LCP)
99137007Sbrian          dl->stayonline = 1;
99236285Sbrian      }
99352029Sbrian      break;
99436285Sbrian
99536285Sbrian    default:
99637007Sbrian      datalink_ComeDown(dl, how);
99736285Sbrian  }
99836285Sbrian}
99936285Sbrian
100036285Sbrianvoid
100137007Sbriandatalink_Down(struct datalink *dl, int how)
100236285Sbrian{
100336285Sbrian  /* Carrier is lost */
100436285Sbrian  switch (dl->state) {
100536285Sbrian    case DATALINK_OPEN:
100636285Sbrian      peerid_Init(&dl->peer);
100737060Sbrian      fsm2initial(&dl->physical->link.ccp.fsm);
100836285Sbrian      /* fall through */
100936285Sbrian
101038174Sbrian    case DATALINK_CBCP:
101136285Sbrian    case DATALINK_AUTH:
101236285Sbrian    case DATALINK_LCP:
101337060Sbrian      fsm2initial(&dl->physical->link.lcp.fsm);
101436285Sbrian      /* fall through */
101536285Sbrian
101636285Sbrian    default:
101737007Sbrian      datalink_ComeDown(dl, how);
101836285Sbrian  }
101936285Sbrian}
102036285Sbrian
102136285Sbrianvoid
102236285Sbriandatalink_StayDown(struct datalink *dl)
102336285Sbrian{
102436285Sbrian  dl->reconnect_tries = 0;
102536285Sbrian}
102636285Sbrian
102737007Sbrianvoid
102837007Sbriandatalink_DontHangup(struct datalink *dl)
102937007Sbrian{
103037012Sbrian  if (dl->state >= DATALINK_LCP)
103137012Sbrian    dl->stayonline = 1;
103237007Sbrian}
103337007Sbrian
103436285Sbrianint
103536285Sbriandatalink_Show(struct cmdargs const *arg)
103636285Sbrian{
103736285Sbrian  prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
103838174Sbrian  prompt_Printf(arg->prompt, " State:              %s\n",
103936285Sbrian                datalink_State(arg->cx));
104038174Sbrian  prompt_Printf(arg->prompt, " Peer name:          ");
104136285Sbrian  if (*arg->cx->peer.authname)
104236285Sbrian    prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
104336285Sbrian  else if (arg->cx->state == DATALINK_OPEN)
104436285Sbrian    prompt_Printf(arg->prompt, "None requested\n");
104536285Sbrian  else
104636285Sbrian    prompt_Printf(arg->prompt, "N/A\n");
104738174Sbrian  prompt_Printf(arg->prompt, " Discriminator:      %s\n",
104836285Sbrian                mp_Enddisc(arg->cx->peer.enddisc.class,
104936285Sbrian                           arg->cx->peer.enddisc.address,
105036285Sbrian                           arg->cx->peer.enddisc.len));
105136285Sbrian
105236285Sbrian  prompt_Printf(arg->prompt, "\nDefaults:\n");
105338174Sbrian  prompt_Printf(arg->prompt, " Phone List:         %s\n",
105436285Sbrian                arg->cx->cfg.phone.list);
105536285Sbrian  if (arg->cx->cfg.dial.max)
105638174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
105736285Sbrian                  arg->cx->cfg.dial.max);
105836285Sbrian  else
105938174Sbrian    prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
106044261Sbrian  if (arg->cx->cfg.dial.next_timeout >= 0)
106136285Sbrian    prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
106236285Sbrian  else
106336285Sbrian    prompt_Printf(arg->prompt, "random/");
106444261Sbrian  if (arg->cx->cfg.dial.timeout >= 0)
106536285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
106636285Sbrian  else
106736285Sbrian    prompt_Printf(arg->prompt, "random\n");
106838174Sbrian  prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
106936285Sbrian                arg->cx->cfg.reconnect.max);
107036285Sbrian  if (arg->cx->cfg.reconnect.timeout > 0)
107136285Sbrian    prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
107236285Sbrian  else
107336285Sbrian    prompt_Printf(arg->prompt, "random\n");
107438174Sbrian  prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
107538174Sbrian                PHYS_DIRECT ?  "accepted: " : "requested:");
107638174Sbrian  if (!arg->cx->cfg.callback.opmask)
107738174Sbrian    prompt_Printf(arg->prompt, "none\n");
107838174Sbrian  else {
107938174Sbrian    int comma = 0;
108038174Sbrian
108138174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
108238174Sbrian      prompt_Printf(arg->prompt, "none");
108338174Sbrian      comma = 1;
108438174Sbrian    }
108538174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
108638174Sbrian      prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
108738174Sbrian      comma = 1;
108838174Sbrian    }
108938174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
109038174Sbrian      prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
109138174Sbrian      if (arg->cx->physical->type != PHYS_DIRECT)
109238174Sbrian        prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
109338174Sbrian      comma = 1;
109438174Sbrian    }
109538174Sbrian    if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
109638174Sbrian      prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
109738174Sbrian      prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
109838174Sbrian                    arg->cx->cfg.cbcp.delay);
109940483Sbrian      prompt_Printf(arg->prompt, "                     phone: ");
110040483Sbrian      if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
110140483Sbrian        if (arg->cx->physical->type & PHYS_DIRECT)
110240483Sbrian          prompt_Printf(arg->prompt, "Caller decides\n");
110340483Sbrian        else
110440483Sbrian          prompt_Printf(arg->prompt, "Dialback server decides\n");
110540483Sbrian      } else
110640483Sbrian        prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
110738174Sbrian      prompt_Printf(arg->prompt, "                     timeout: %lds\n",
110838174Sbrian                    arg->cx->cfg.cbcp.fsmretry);
110938174Sbrian    } else
111038174Sbrian      prompt_Printf(arg->prompt, "\n");
111138174Sbrian  }
111238174Sbrian
111338174Sbrian  prompt_Printf(arg->prompt, " Dial Script:        %s\n",
111436285Sbrian                arg->cx->cfg.script.dial);
111538174Sbrian  prompt_Printf(arg->prompt, " Login Script:       %s\n",
111636285Sbrian                arg->cx->cfg.script.login);
111752488Sbrian  prompt_Printf(arg->prompt, " Logout Script:      %s\n",
111852488Sbrian                arg->cx->cfg.script.logout);
111938174Sbrian  prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
112036285Sbrian                arg->cx->cfg.script.hangup);
112136285Sbrian  return 0;
112236285Sbrian}
112336285Sbrian
112436285Sbrianint
112536285Sbriandatalink_SetReconnect(struct cmdargs const *arg)
112636285Sbrian{
112736285Sbrian  if (arg->argc == arg->argn+2) {
112836285Sbrian    arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
112936285Sbrian    arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
113036285Sbrian    return 0;
113136285Sbrian  }
113236285Sbrian  return -1;
113336285Sbrian}
113436285Sbrian
113536285Sbrianint
113636285Sbriandatalink_SetRedial(struct cmdargs const *arg)
113736285Sbrian{
113844468Sbrian  const char *sep, *osep;
113944468Sbrian  int timeout, inc, maxinc, tries;
114036285Sbrian
114136285Sbrian  if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
114236285Sbrian    if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
114336285Sbrian	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
114436285Sbrian      arg->cx->cfg.dial.timeout = -1;
114536285Sbrian      randinit();
114636285Sbrian    } else {
114736285Sbrian      timeout = atoi(arg->argv[arg->argn]);
114836285Sbrian
114936285Sbrian      if (timeout >= 0)
115036285Sbrian	arg->cx->cfg.dial.timeout = timeout;
115136285Sbrian      else {
115236285Sbrian	log_Printf(LogWARN, "Invalid redial timeout\n");
115336285Sbrian	return -1;
115436285Sbrian      }
115536285Sbrian    }
115636285Sbrian
115744468Sbrian    sep = strchr(arg->argv[arg->argn], '+');
115844468Sbrian    if (sep) {
115944468Sbrian      inc = atoi(++sep);
116044468Sbrian      osep = sep;
116144468Sbrian      if (inc >= 0)
116244468Sbrian        arg->cx->cfg.dial.inc = inc;
116344468Sbrian      else {
116444468Sbrian        log_Printf(LogWARN, "Invalid timeout increment\n");
116544468Sbrian        return -1;
116644468Sbrian      }
116744468Sbrian      sep = strchr(sep, '-');
116844468Sbrian      if (sep) {
116944468Sbrian        maxinc = atoi(++sep);
117044468Sbrian        if (maxinc >= 0)
117144468Sbrian          arg->cx->cfg.dial.maxinc = maxinc;
117244468Sbrian        else {
117344468Sbrian          log_Printf(LogWARN, "Invalid maximum timeout increments\n");
117444468Sbrian          return -1;
117544468Sbrian        }
117644468Sbrian      } else {
117744468Sbrian        /* Default timeout increment */
117844468Sbrian        arg->cx->cfg.dial.maxinc = 10;
117944468Sbrian        sep = osep;
118044468Sbrian      }
118144468Sbrian    } else {
118244468Sbrian      /* Default timeout increment & max increment */
118344468Sbrian      arg->cx->cfg.dial.inc = 0;
118444468Sbrian      arg->cx->cfg.dial.maxinc = 10;
118544468Sbrian      sep = arg->argv[arg->argn];
118644468Sbrian    }
118744468Sbrian
118844468Sbrian    sep = strchr(sep, '.');
118944468Sbrian    if (sep) {
119044468Sbrian      if (strcasecmp(++sep, "random") == 0) {
119136285Sbrian	arg->cx->cfg.dial.next_timeout = -1;
119236285Sbrian	randinit();
119336285Sbrian      } else {
119444468Sbrian	timeout = atoi(sep);
119536285Sbrian	if (timeout >= 0)
119636285Sbrian	  arg->cx->cfg.dial.next_timeout = timeout;
119736285Sbrian	else {
119836285Sbrian	  log_Printf(LogWARN, "Invalid next redial timeout\n");
119936285Sbrian	  return -1;
120036285Sbrian	}
120136285Sbrian      }
120236285Sbrian    } else
120336285Sbrian      /* Default next timeout */
120436285Sbrian      arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
120536285Sbrian
120636285Sbrian    if (arg->argc == arg->argn+2) {
120736285Sbrian      tries = atoi(arg->argv[arg->argn+1]);
120836285Sbrian
120936285Sbrian      if (tries >= 0) {
121036285Sbrian	arg->cx->cfg.dial.max = tries;
121136285Sbrian      } else {
121236285Sbrian	log_Printf(LogWARN, "Invalid retry value\n");
121336285Sbrian	return 1;
121436285Sbrian      }
121536285Sbrian    }
121636285Sbrian    return 0;
121736285Sbrian  }
121844468Sbrian
121936285Sbrian  return -1;
122036285Sbrian}
122136285Sbrian
122236285Sbrianstatic const char *states[] = {
122336285Sbrian  "closed",
122436285Sbrian  "opening",
122536285Sbrian  "hangup",
122636285Sbrian  "dial",
122749472Sbrian  "carrier",
122852488Sbrian  "logout",
122936285Sbrian  "login",
123036285Sbrian  "ready",
123136285Sbrian  "lcp",
123236285Sbrian  "auth",
123338174Sbrian  "cbcp",
123436285Sbrian  "open"
123536285Sbrian};
123636285Sbrian
123736285Sbrianconst char *
123836285Sbriandatalink_State(struct datalink *dl)
123936285Sbrian{
124036285Sbrian  if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
124136285Sbrian    return "unknown";
124236285Sbrian  return states[dl->state];
124336285Sbrian}
124436285Sbrian
124536285Sbrianstatic void
124636285Sbriandatalink_NewState(struct datalink *dl, int state)
124736285Sbrian{
124836285Sbrian  if (state != dl->state) {
124936285Sbrian    if (state >= 0 && state < sizeof states / sizeof states[0]) {
125036285Sbrian      log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
125136285Sbrian                 states[state]);
125236285Sbrian      dl->state = state;
125336285Sbrian    } else
125436285Sbrian      log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
125536285Sbrian  }
125636285Sbrian}
125736285Sbrian
125836285Sbrianstruct datalink *
125936285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
126052942Sbrian             int fd, int *auxfd, int *nauxfd)
126136285Sbrian{
126236285Sbrian  struct datalink *dl, *cdl;
126344305Sbrian  struct fsm_retry copy;
126436285Sbrian  char *oname;
126536285Sbrian
126636285Sbrian  dl = (struct datalink *)iov[(*niov)++].iov_base;
126736285Sbrian  dl->name = iov[*niov].iov_base;
126836285Sbrian
126936285Sbrian  if (dl->name[DATALINK_MAXNAME-1]) {
127036285Sbrian    dl->name[DATALINK_MAXNAME-1] = '\0';
127136285Sbrian    if (strlen(dl->name) == DATALINK_MAXNAME - 1)
127236285Sbrian      log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
127336285Sbrian  }
127436285Sbrian
127536285Sbrian  /* Make sure the name is unique ! */
127636285Sbrian  oname = NULL;
127736285Sbrian  do {
127836285Sbrian    for (cdl = bundle->links; cdl; cdl = cdl->next)
127936285Sbrian      if (!strcasecmp(dl->name, cdl->name)) {
128036285Sbrian        if (oname)
128136285Sbrian          free(datalink_NextName(dl));
128236285Sbrian        else
128336285Sbrian          oname = datalink_NextName(dl);
128436285Sbrian        break;	/* Keep renaming 'till we have no conflicts */
128536285Sbrian      }
128636285Sbrian  } while (cdl);
128736285Sbrian
128836285Sbrian  if (oname) {
128936285Sbrian    log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
129036285Sbrian    free(oname);
129136285Sbrian  } else {
129236285Sbrian    dl->name = strdup(dl->name);
129336285Sbrian    free(iov[*niov].iov_base);
129436285Sbrian  }
129536285Sbrian  (*niov)++;
129636285Sbrian
129736285Sbrian  dl->desc.type = DATALINK_DESCRIPTOR;
129836285Sbrian  dl->desc.UpdateSet = datalink_UpdateSet;
129936285Sbrian  dl->desc.IsSet = datalink_IsSet;
130036285Sbrian  dl->desc.Read = datalink_Read;
130136285Sbrian  dl->desc.Write = datalink_Write;
130236285Sbrian
130336285Sbrian  mp_linkInit(&dl->mp);
130436285Sbrian  *dl->phone.list = '\0';
130536285Sbrian  dl->phone.next = NULL;
130636285Sbrian  dl->phone.alt = NULL;
130736285Sbrian  dl->phone.chosen = "N/A";
130836285Sbrian
130936285Sbrian  dl->bundle = bundle;
131036285Sbrian  dl->next = NULL;
131144468Sbrian  memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
131244468Sbrian  dl->dial.tries = 0;
131336285Sbrian  dl->reconnect_tries = 0;
131436285Sbrian  dl->parent = &bundle->fsm;
131536285Sbrian  dl->fsmp.LayerStart = datalink_LayerStart;
131636285Sbrian  dl->fsmp.LayerUp = datalink_LayerUp;
131736285Sbrian  dl->fsmp.LayerDown = datalink_LayerDown;
131836285Sbrian  dl->fsmp.LayerFinish = datalink_LayerFinish;
131936285Sbrian  dl->fsmp.object = dl;
132036285Sbrian
132152942Sbrian  dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
132236285Sbrian
132336285Sbrian  if (!dl->physical) {
132436285Sbrian    free(dl->name);
132536285Sbrian    free(dl);
132636285Sbrian    dl = NULL;
132736285Sbrian  } else {
132844305Sbrian    copy = dl->pap.cfg.fsm;
132943693Sbrian    pap_Init(&dl->pap, dl->physical);
133044305Sbrian    dl->pap.cfg.fsm = copy;
133143693Sbrian
133244305Sbrian    copy = dl->chap.auth.cfg.fsm;
133343693Sbrian    chap_Init(&dl->chap, dl->physical);
133444305Sbrian    dl->chap.auth.cfg.fsm = copy;
133543693Sbrian
133638174Sbrian    cbcp_Init(&dl->cbcp, dl->physical);
133736285Sbrian
133852488Sbrian    memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
133954055Sbrian    chat_Init(&dl->chat, dl->physical);
134052488Sbrian
134136285Sbrian    log_Printf(LogPHASE, "%s: Transferred in %s state\n",
134236285Sbrian              dl->name, datalink_State(dl));
134336285Sbrian  }
134436285Sbrian
134536285Sbrian  return dl;
134636285Sbrian}
134736285Sbrian
134836285Sbrianint
134936450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
135053684Sbrian             int *auxfd, int *nauxfd)
135136285Sbrian{
135236285Sbrian  /* If `dl' is NULL, we're allocating before a Fromiov() */
135336285Sbrian  int link_fd;
135436285Sbrian
135536285Sbrian  if (dl) {
135644468Sbrian    timer_Stop(&dl->dial.timer);
135738174Sbrian    /* The following is purely for the sake of paranoia */
135838174Sbrian    cbcp_Down(&dl->cbcp);
135936285Sbrian    timer_Stop(&dl->pap.authtimer);
136036285Sbrian    timer_Stop(&dl->chap.auth.authtimer);
136136285Sbrian  }
136236285Sbrian
136336285Sbrian  if (*niov >= maxiov - 1) {
136436285Sbrian    log_Printf(LogERROR, "Toiov: No room for datalink !\n");
136536285Sbrian    if (dl) {
136636285Sbrian      free(dl->name);
136736285Sbrian      free(dl);
136836285Sbrian    }
136936285Sbrian    return -1;
137036285Sbrian  }
137136285Sbrian
137253684Sbrian  iov[*niov].iov_base = (void *)dl;
137336285Sbrian  iov[(*niov)++].iov_len = sizeof *dl;
137453684Sbrian  iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
137536285Sbrian  iov[(*niov)++].iov_len = DATALINK_MAXNAME;
137636285Sbrian
137752942Sbrian  link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
137853684Sbrian                         nauxfd);
137936285Sbrian
138036285Sbrian  if (link_fd == -1 && dl) {
138136285Sbrian    free(dl->name);
138236285Sbrian    free(dl);
138336285Sbrian  }
138436285Sbrian
138536285Sbrian  return link_fd;
138636285Sbrian}
138736285Sbrian
138836285Sbrianvoid
138936285Sbriandatalink_Rename(struct datalink *dl, const char *name)
139036285Sbrian{
139136285Sbrian  free(dl->name);
139236285Sbrian  dl->physical->link.name = dl->name = strdup(name);
139336285Sbrian}
139436285Sbrian
139536285Sbrianchar *
139636285Sbriandatalink_NextName(struct datalink *dl)
139736285Sbrian{
139836285Sbrian  int f, n;
139936285Sbrian  char *name, *oname;
140036285Sbrian
140136285Sbrian  n = strlen(dl->name);
140236285Sbrian  name = (char *)malloc(n+3);
140336285Sbrian  for (f = n - 1; f >= 0; f--)
140436285Sbrian    if (!isdigit(dl->name[f]))
140536285Sbrian      break;
140636285Sbrian  n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
140736285Sbrian  sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
140836285Sbrian  oname = dl->name;
140936345Sbrian  dl->name = name;
141036345Sbrian  /* our physical link name isn't updated (it probably isn't created yet) */
141136285Sbrian  return oname;
141236285Sbrian}
141336285Sbrian
141436285Sbrianint
141536285Sbriandatalink_SetMode(struct datalink *dl, int mode)
141636285Sbrian{
141736285Sbrian  if (!physical_SetMode(dl->physical, mode))
141836285Sbrian    return 0;
141936285Sbrian  if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
142036285Sbrian    dl->script.run = 0;
142136285Sbrian  if (dl->physical->type == PHYS_DIRECT)
142236285Sbrian    dl->reconnect_tries = 0;
142353830Sbrian  if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
142453830Sbrian      dl->state <= DATALINK_READY)
142536285Sbrian    datalink_Up(dl, 1, 1);
142636285Sbrian  return 1;
142736285Sbrian}
142844468Sbrian
142944468Sbrianint
143044468Sbriandatalink_GetDialTimeout(struct datalink *dl)
143144468Sbrian{
143244468Sbrian  int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
143344468Sbrian
143444468Sbrian  if (dl->dial.incs < dl->cfg.dial.maxinc)
143544468Sbrian    dl->dial.incs++;
143644468Sbrian
143744468Sbrian  return result;
143844468Sbrian}
1439