xref: /freebsd/usr.sbin/ppp/datalink.c (revision b42135de)
13006ec67SBrian Somers /*-
23006ec67SBrian Somers  * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
33006ec67SBrian Somers  * All rights reserved.
43006ec67SBrian Somers  *
53006ec67SBrian Somers  * Redistribution and use in source and binary forms, with or without
63006ec67SBrian Somers  * modification, are permitted provided that the following conditions
73006ec67SBrian Somers  * are met:
83006ec67SBrian Somers  * 1. Redistributions of source code must retain the above copyright
93006ec67SBrian Somers  *    notice, this list of conditions and the following disclaimer.
103006ec67SBrian Somers  * 2. Redistributions in binary form must reproduce the above copyright
113006ec67SBrian Somers  *    notice, this list of conditions and the following disclaimer in the
123006ec67SBrian Somers  *    documentation and/or other materials provided with the distribution.
133006ec67SBrian Somers  *
143006ec67SBrian Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
153006ec67SBrian Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
163006ec67SBrian Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
173006ec67SBrian Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
183006ec67SBrian Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
193006ec67SBrian Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
203006ec67SBrian Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
213006ec67SBrian Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
223006ec67SBrian Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
233006ec67SBrian Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
243006ec67SBrian Somers  * SUCH DAMAGE.
253006ec67SBrian Somers  *
2697d92980SPeter Wemm  * $FreeBSD$
273006ec67SBrian Somers  */
283006ec67SBrian Somers 
29972a1bcfSBrian Somers #include <sys/param.h>
303006ec67SBrian Somers #include <netinet/in.h>
31eaa4df37SBrian Somers #include <netinet/in_systm.h>
32eaa4df37SBrian Somers #include <netinet/ip.h>
331fa665f5SBrian Somers #include <sys/un.h>
343006ec67SBrian Somers 
356f384573SBrian Somers #include <ctype.h>
36c7cc5030SBrian Somers #include <stdio.h>
373006ec67SBrian Somers #include <stdlib.h>
383006ec67SBrian Somers #include <string.h>
3996c9bb21SBrian Somers #include <sys/uio.h>
403006ec67SBrian Somers #include <termios.h>
413006ec67SBrian Somers 
425d9e6103SBrian Somers #include "layer.h"
433006ec67SBrian Somers #include "mbuf.h"
443006ec67SBrian Somers #include "log.h"
453006ec67SBrian Somers #include "defs.h"
463006ec67SBrian Somers #include "timer.h"
473006ec67SBrian Somers #include "fsm.h"
483006ec67SBrian Somers #include "lcp.h"
493006ec67SBrian Somers #include "descriptor.h"
50879ed6faSBrian Somers #include "lqr.h"
513006ec67SBrian Somers #include "hdlc.h"
523006ec67SBrian Somers #include "async.h"
533006ec67SBrian Somers #include "throughput.h"
543b0f8d2eSBrian Somers #include "ccp.h"
553006ec67SBrian Somers #include "link.h"
563006ec67SBrian Somers #include "physical.h"
575828db6dSBrian Somers #include "iplist.h"
58eaa4df37SBrian Somers #include "slcompress.h"
595828db6dSBrian Somers #include "ipcp.h"
605ca5389aSBrian Somers #include "filter.h"
613b0f8d2eSBrian Somers #include "mp.h"
62972a1bcfSBrian Somers #ifndef NORADIUS
63972a1bcfSBrian Somers #include "radius.h"
64972a1bcfSBrian Somers #endif
653006ec67SBrian Somers #include "bundle.h"
663006ec67SBrian Somers #include "chat.h"
67e2ebb036SBrian Somers #include "auth.h"
68c7cc5030SBrian Somers #include "prompt.h"
695d9e6103SBrian Somers #include "proto.h"
70e2ebb036SBrian Somers #include "pap.h"
71e2ebb036SBrian Somers #include "chap.h"
72565e35e5SBrian Somers #include "command.h"
7392b09558SBrian Somers #include "cbcp.h"
74e2ebb036SBrian Somers #include "datalink.h"
75c7cc5030SBrian Somers 
76030e4ebbSBrian Somers static void datalink_LoginDone(struct datalink *);
779ae58882SBrian Somers static void datalink_NewState(struct datalink *, int);
783006ec67SBrian Somers 
793006ec67SBrian Somers static void
803006ec67SBrian Somers datalink_OpenTimeout(void *v)
813006ec67SBrian Somers {
823006ec67SBrian Somers   struct datalink *dl = (struct datalink *)v;
833006ec67SBrian Somers 
84c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
853006ec67SBrian Somers   if (dl->state == DATALINK_OPENING)
86b42135deSBrian Somers     log_Printf(LogCHAT, "%s: Redial timer expired.\n", dl->name);
873006ec67SBrian Somers }
883006ec67SBrian Somers 
89b5c347a3SBrian Somers static int
903006ec67SBrian Somers datalink_StartDialTimer(struct datalink *dl, int Timeout)
913006ec67SBrian Somers {
92b5c347a3SBrian Somers   int result = Timeout;
933006ec67SBrian Somers 
94c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
953006ec67SBrian Somers   if (Timeout) {
963006ec67SBrian Somers     if (Timeout > 0)
97c11e57a3SBrian Somers       dl->dial.timer.load = Timeout * SECTICKS;
98b5c347a3SBrian Somers     else {
99b5c347a3SBrian Somers       result = (random() % DIAL_TIMEOUT) + 1;
100c11e57a3SBrian Somers       dl->dial.timer.load = result * SECTICKS;
101b5c347a3SBrian Somers     }
102c11e57a3SBrian Somers     dl->dial.timer.func = datalink_OpenTimeout;
103c11e57a3SBrian Somers     dl->dial.timer.name = "dial";
104c11e57a3SBrian Somers     dl->dial.timer.arg = dl;
105c11e57a3SBrian Somers     timer_Start(&dl->dial.timer);
1063006ec67SBrian Somers     if (dl->state == DATALINK_OPENING)
107dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
1083006ec67SBrian Somers                 dl->name, Timeout);
1093006ec67SBrian Somers   }
110b5c347a3SBrian Somers   return result;
1113006ec67SBrian Somers }
1123006ec67SBrian Somers 
1133006ec67SBrian Somers static void
1143006ec67SBrian Somers datalink_HangupDone(struct datalink *dl)
1153006ec67SBrian Somers {
116030e4ebbSBrian Somers   if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
1175d9e6103SBrian Somers       dl->physical->fd != -1) {
1185d9e6103SBrian Somers     /* Don't close our device if the link is dedicated */
119030e4ebbSBrian Somers     datalink_LoginDone(dl);
120030e4ebbSBrian Somers     return;
121030e4ebbSBrian Somers   }
122030e4ebbSBrian Somers 
123ffcfaec7SBrian Somers   chat_Finish(&dl->chat);
1245d9e6103SBrian Somers   physical_Close(dl->physical);
125565e35e5SBrian Somers   dl->phone.chosen = "N/A";
1263006ec67SBrian Somers 
12792b09558SBrian Somers   if (dl->cbcp.required) {
12892b09558SBrian Somers     log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
12992b09558SBrian Somers     dl->cfg.callback.opmask = 0;
13092b09558SBrian Somers     strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
13192b09558SBrian Somers             sizeof dl->cfg.phone.list - 1);
13292b09558SBrian Somers     dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
13392b09558SBrian Somers     dl->phone.alt = dl->phone.next = NULL;
13492b09558SBrian Somers     dl->reconnect_tries = dl->cfg.reconnect.max;
135c11e57a3SBrian Somers     dl->dial.tries = dl->cfg.dial.max;
136c11e57a3SBrian Somers     dl->dial.incs = 0;
13792b09558SBrian Somers     dl->script.run = 1;
13892b09558SBrian Somers     dl->script.packetmode = 1;
13992b09558SBrian Somers     if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
14092b09558SBrian Somers       log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
14192b09558SBrian Somers     bundle_LinksRemoved(dl->bundle);
142c11e57a3SBrian Somers     /* if dial.timeout is < 0 (random), we don't override fsm.delay */
14392b09558SBrian Somers     if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
14492b09558SBrian Somers       dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
14592b09558SBrian Somers     datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
14692b09558SBrian Somers     cbcp_Down(&dl->cbcp);
14792b09558SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
1481b02dfb4SBrian Somers     if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
1491b02dfb4SBrian Somers         bundle_Phase(dl->bundle) == PHASE_TERMINATE)
1500ca6f91bSBrian Somers       bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
15192b09558SBrian Somers   } else if (dl->bundle->CleaningUp ||
1526f384573SBrian Somers       (dl->physical->type == PHYS_DIRECT) ||
153c11e57a3SBrian Somers       ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15481358fa3SBrian Somers        !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
1559ae58882SBrian Somers     datalink_NewState(dl, DATALINK_CLOSED);
156c11e57a3SBrian Somers     dl->dial.tries = -1;
157c11e57a3SBrian Somers     dl->dial.incs = 0;
1583006ec67SBrian Somers     dl->reconnect_tries = 0;
1593006ec67SBrian Somers     bundle_LinkClosed(dl->bundle, dl);
1604b567bf2SBrian Somers     if (!dl->bundle->CleaningUp &&
1614b567bf2SBrian Somers         !(dl->physical->type & (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)))
162c11e57a3SBrian Somers       datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1633006ec67SBrian Somers   } else {
1649ae58882SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
1651b02dfb4SBrian Somers     if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
1661b02dfb4SBrian Somers         bundle_Phase(dl->bundle) == PHASE_TERMINATE)
1670ca6f91bSBrian Somers       bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
168c11e57a3SBrian Somers     if (dl->dial.tries < 0) {
169565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
170c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
171c11e57a3SBrian Somers       dl->dial.incs = 0;
172abff9baeSBrian Somers       dl->reconnect_tries--;
173b42135deSBrian Somers       log_Printf(LogCHAT, "%s: Reconnect try %d of %d\n",
174b42135deSBrian Somers                  dl->name, dl->cfg.reconnect.max - dl->reconnect_tries,
175b42135deSBrian Somers                  dl->cfg.reconnect.max);
176b42135deSBrian Somers       bundle_Notify(dl->bundle, EX_RECONNECT);
177abff9baeSBrian Somers     } else {
178f9545805SBrian Somers       if (dl->phone.next == NULL)
179c11e57a3SBrian Somers         datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1803006ec67SBrian Somers       else
181565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
182b42135deSBrian Somers       bundle_Notify(dl->bundle, EX_REDIAL);
1833006ec67SBrian Somers     }
184abff9baeSBrian Somers   }
185abff9baeSBrian Somers }
1863006ec67SBrian Somers 
187eb6e5e05SBrian Somers const char *
188f9545805SBrian Somers datalink_ChoosePhoneNumber(struct datalink *dl)
189f9545805SBrian Somers {
190f9545805SBrian Somers   char *phone;
191f9545805SBrian Somers 
192f9545805SBrian Somers   if (dl->phone.alt == NULL) {
193f9545805SBrian Somers     if (dl->phone.next == NULL) {
194f9545805SBrian Somers       strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
195f9545805SBrian Somers       dl->phone.list[sizeof dl->phone.list - 1] = '\0';
19608da4867SBrian Somers       if (*dl->phone.list == '\0')
19708da4867SBrian Somers         return "";
198f9545805SBrian Somers       dl->phone.next = dl->phone.list;
199f9545805SBrian Somers     }
200f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
201f9545805SBrian Somers   }
202f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
203f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
204f9545805SBrian Somers   if (*phone)
205b42135deSBrian Somers     log_Printf(LogCHAT, "Phone: %s\n", phone);
206f9545805SBrian Somers   return phone;
207f9545805SBrian Somers }
208f9545805SBrian Somers 
209c5a5a6caSBrian Somers static void
210c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
211c5a5a6caSBrian Somers {
212ffcfaec7SBrian Somers   chat_Finish(&dl->chat);
213c116e0c0SBrian Somers 
214d345321bSBrian Somers   if (!dl->script.packetmode) {
215c11e57a3SBrian Somers     dl->dial.tries = -1;
216c11e57a3SBrian Somers     dl->dial.incs = 0;
2179ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
2185d9e6103SBrian Somers   } else if (!physical_Raw(dl->physical)) {
219c11e57a3SBrian Somers     dl->dial.tries = 0;
220dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
221c5a5a6caSBrian Somers     if (dl->script.run) {
222c116e0c0SBrian Somers       datalink_NewState(dl, DATALINK_LOGOUT);
223c39aa54eSBrian Somers       if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
224c39aa54eSBrian Somers         log_Printf(LogWARN, "Invalid logout script\n");
225030e4ebbSBrian Somers     } else {
2266815097bSBrian Somers       physical_StopDeviceTimer(dl->physical);
227030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
228030e4ebbSBrian Somers         /* force a redial timeout */
2295d9e6103SBrian Somers         physical_Close(dl->physical);
230c5a5a6caSBrian Somers       datalink_HangupDone(dl);
231030e4ebbSBrian Somers     }
232c5a5a6caSBrian Somers   } else {
233c11e57a3SBrian Somers     dl->dial.tries = -1;
234c11e57a3SBrian Somers     dl->dial.incs = 0;
235c7cc5030SBrian Somers 
236643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
2373b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
2383b0f8d2eSBrian Somers 
239cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
240cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
2413b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
242503a7782SBrian Somers 
2439ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
244dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
245dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
246c5a5a6caSBrian Somers   }
247c5a5a6caSBrian Somers }
248c5a5a6caSBrian Somers 
2493006ec67SBrian Somers static int
250f013f33eSBrian Somers datalink_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
2513006ec67SBrian Somers                    int *n)
2523006ec67SBrian Somers {
2533006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2543006ec67SBrian Somers   int result;
2553006ec67SBrian Somers 
256310c3babSBrian Somers   result = 0;
2573006ec67SBrian Somers   switch (dl->state) {
2583006ec67SBrian Somers     case DATALINK_CLOSED:
259f6a4e748SBrian Somers       if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
260f6a4e748SBrian Somers                                  PHYS_FOREGROUND|PHYS_DDIAL)) &&
261194c225dSBrian Somers           !dl->bundle->CleaningUp)
262565e35e5SBrian Somers         /*
26381358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
264f6a4e748SBrian Somers          * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
265f6a4e748SBrian Somers          * DATALINK_CLOSED.  Go to DATALINK_OPENING via datalink_Up()
266f6a4e748SBrian Somers          * and fall through.
267565e35e5SBrian Somers          */
268565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
269565e35e5SBrian Somers       else
270310c3babSBrian Somers         break;
271565e35e5SBrian Somers       /* fall through */
2723006ec67SBrian Somers 
2733006ec67SBrian Somers     case DATALINK_OPENING:
274c11e57a3SBrian Somers       if (dl->dial.timer.state != TIMER_RUNNING) {
275c11e57a3SBrian Somers         if (--dl->dial.tries < 0)
276c11e57a3SBrian Somers           dl->dial.tries = 0;
2775d9e6103SBrian Somers         if (physical_Open(dl->physical, dl->bundle) >= 0) {
278bf1d3ff6SBrian Somers           log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
279bf1d3ff6SBrian Somers                            "Type `~?' for help\r\n", dl->name,
280bf1d3ff6SBrian Somers                            dl->physical->name.full);
281c5a5a6caSBrian Somers           if (dl->script.run) {
2829ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
283c39aa54eSBrian Somers             if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
284c39aa54eSBrian Somers                             *dl->cfg.script.dial ?
285c39aa54eSBrian Somers                             datalink_ChoosePhoneNumber(dl) : ""))
286c39aa54eSBrian Somers               log_Printf(LogWARN, "Invalid dial script\n");
28781358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
288565e35e5SBrian Somers                 dl->cfg.dial.max)
289dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
290c11e57a3SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial.tries,
291565e35e5SBrian Somers                         dl->cfg.dial.max);
292c5a5a6caSBrian Somers           } else
29314e34a55SBrian Somers             datalink_NewState(dl, DATALINK_CARRIER);
2948b09cf1cSBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
2953006ec67SBrian Somers         } else {
29681358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
297565e35e5SBrian Somers               dl->cfg.dial.max)
2985d9e6103SBrian Somers             log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
299c11e57a3SBrian Somers                        dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
3003006ec67SBrian Somers           else
3015d9e6103SBrian Somers             log_Printf(LogCHAT, "Failed to open device\n");
3023006ec67SBrian Somers 
3033b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
30481358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
305c11e57a3SBrian Somers                dl->cfg.dial.max && dl->dial.tries == 0)) {
3069ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
3073006ec67SBrian Somers             dl->reconnect_tries = 0;
308c11e57a3SBrian Somers             dl->dial.tries = -1;
309bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s\n",
310bf1d3ff6SBrian Somers                              dl->physical->name.full);
3115b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
312c5a5a6caSBrian Somers           }
313bf1d3ff6SBrian Somers           if (!dl->bundle->CleaningUp) {
314c11e57a3SBrian Somers             int timeout;
315c11e57a3SBrian Somers 
316c11e57a3SBrian Somers             timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
317b42135deSBrian Somers             bundle_Notify(dl->bundle, EX_REDIAL);
318bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
319b5c347a3SBrian Somers                              dl->physical->name.full, timeout);
3203006ec67SBrian Somers           }
3213006ec67SBrian Somers         }
322bf1d3ff6SBrian Somers       }
323310c3babSBrian Somers       break;
3243006ec67SBrian Somers 
325eb6e5e05SBrian Somers     case DATALINK_CARRIER:
326eb6e5e05SBrian Somers       /* Wait for carrier on the device */
327eb6e5e05SBrian Somers       switch (physical_AwaitCarrier(dl->physical)) {
328eb6e5e05SBrian Somers         case CARRIER_PENDING:
329eb6e5e05SBrian Somers           log_Printf(LogDEBUG, "Waiting for carrier\n");
330eb6e5e05SBrian Somers           return 0;	/* A device timer is running to wake us up again */
331eb6e5e05SBrian Somers 
332eb6e5e05SBrian Somers         case CARRIER_OK:
33314e34a55SBrian Somers           if (dl->script.run) {
334eb6e5e05SBrian Somers             datalink_NewState(dl, DATALINK_LOGIN);
335c39aa54eSBrian Somers             if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
336c39aa54eSBrian Somers               log_Printf(LogWARN, "Invalid login script\n");
33714e34a55SBrian Somers           } else
33814e34a55SBrian Somers             datalink_LoginDone(dl);
339eb6e5e05SBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
340eb6e5e05SBrian Somers 
341eb6e5e05SBrian Somers         case CARRIER_LOST:
342eb6e5e05SBrian Somers           physical_Offline(dl->physical);	/* Is this required ? */
34314e34a55SBrian Somers           if (dl->script.run) {
34414e34a55SBrian Somers             datalink_NewState(dl, DATALINK_HANGUP);
345c39aa54eSBrian Somers             if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
346c39aa54eSBrian Somers               log_Printf(LogWARN, "Invalid hangup script\n");
347eb6e5e05SBrian Somers             return datalink_UpdateSet(d, r, w, e, n);
348da8b7034SBrian Somers           } else {
349da8b7034SBrian Somers             datalink_HangupDone(dl);
350da8b7034SBrian Somers             return 0;	/* Maybe bundle_CleanDatalinks() has something to do */
351da8b7034SBrian Somers           }
352eb6e5e05SBrian Somers       }
353eb6e5e05SBrian Somers 
3543006ec67SBrian Somers     case DATALINK_HANGUP:
3553006ec67SBrian Somers     case DATALINK_DIAL:
356c116e0c0SBrian Somers     case DATALINK_LOGOUT:
3573006ec67SBrian Somers     case DATALINK_LOGIN:
3583006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
3593006ec67SBrian Somers       switch (dl->chat.state) {
3603006ec67SBrian Somers         case CHAT_DONE:
3613006ec67SBrian Somers           /* script succeeded */
3623006ec67SBrian Somers           switch(dl->state) {
3633006ec67SBrian Somers             case DATALINK_HANGUP:
3643006ec67SBrian Somers               datalink_HangupDone(dl);
3653006ec67SBrian Somers               break;
3663006ec67SBrian Somers             case DATALINK_DIAL:
367eb6e5e05SBrian Somers               datalink_NewState(dl, DATALINK_CARRIER);
3681342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
369c116e0c0SBrian Somers             case DATALINK_LOGOUT:
370c116e0c0SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
371c116e0c0SBrian Somers               physical_Offline(dl->physical);
372c39aa54eSBrian Somers               if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
373c39aa54eSBrian Somers                 log_Printf(LogWARN, "Invalid hangup script\n");
374c116e0c0SBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3753006ec67SBrian Somers             case DATALINK_LOGIN:
37647dd77c1SBrian Somers               dl->phone.alt = NULL;
377c5a5a6caSBrian Somers               datalink_LoginDone(dl);
378b51a60ccSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3793006ec67SBrian Somers           }
3803006ec67SBrian Somers           break;
3813006ec67SBrian Somers         case CHAT_FAILED:
3823006ec67SBrian Somers           /* Going down - script failed */
383dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
3843006ec67SBrian Somers           switch(dl->state) {
3853006ec67SBrian Somers             case DATALINK_HANGUP:
3863006ec67SBrian Somers               datalink_HangupDone(dl);
3873006ec67SBrian Somers               break;
3883006ec67SBrian Somers             case DATALINK_DIAL:
389c116e0c0SBrian Somers             case DATALINK_LOGOUT:
3903006ec67SBrian Somers             case DATALINK_LOGIN:
3919ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
392c116e0c0SBrian Somers               physical_Offline(dl->physical);
393c39aa54eSBrian Somers               if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
394c39aa54eSBrian Somers                 log_Printf(LogWARN, "Invalid hangup script\n");
3951342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3963006ec67SBrian Somers           }
3973006ec67SBrian Somers           break;
3983006ec67SBrian Somers       }
3993006ec67SBrian Somers       break;
400c7cc5030SBrian Somers 
401c7cc5030SBrian Somers     case DATALINK_READY:
402e2ebb036SBrian Somers     case DATALINK_LCP:
403e2ebb036SBrian Somers     case DATALINK_AUTH:
40492b09558SBrian Somers     case DATALINK_CBCP:
4053006ec67SBrian Somers     case DATALINK_OPEN:
40658330d7bSBrian Somers       result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
40758330d7bSBrian Somers                descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
4083006ec67SBrian Somers       break;
4093006ec67SBrian Somers   }
4103006ec67SBrian Somers   return result;
4113006ec67SBrian Somers }
4123006ec67SBrian Somers 
413ea722969SBrian Somers int
414ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
415ea722969SBrian Somers {
416ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
417ea722969SBrian Somers }
418ea722969SBrian Somers 
4193006ec67SBrian Somers static int
420f013f33eSBrian Somers datalink_IsSet(struct fdescriptor *d, const fd_set *fdset)
4213006ec67SBrian Somers {
4223006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4233006ec67SBrian Somers 
4243006ec67SBrian Somers   switch (dl->state) {
4253006ec67SBrian Somers     case DATALINK_CLOSED:
426c7cc5030SBrian Somers     case DATALINK_OPENING:
4273006ec67SBrian Somers       break;
428c7cc5030SBrian Somers 
4293006ec67SBrian Somers     case DATALINK_HANGUP:
4303006ec67SBrian Somers     case DATALINK_DIAL:
431c116e0c0SBrian Somers     case DATALINK_LOGOUT:
4323006ec67SBrian Somers     case DATALINK_LOGIN:
4333006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
434c7cc5030SBrian Somers 
435c7cc5030SBrian Somers     case DATALINK_READY:
436e2ebb036SBrian Somers     case DATALINK_LCP:
437e2ebb036SBrian Somers     case DATALINK_AUTH:
43892b09558SBrian Somers     case DATALINK_CBCP:
4393006ec67SBrian Somers     case DATALINK_OPEN:
44058330d7bSBrian Somers       return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
44158330d7bSBrian Somers              descriptor_IsSet(&dl->physical->desc, fdset);
4423006ec67SBrian Somers   }
4433006ec67SBrian Somers   return 0;
4443006ec67SBrian Somers }
4453006ec67SBrian Somers 
4463006ec67SBrian Somers static void
447f013f33eSBrian Somers datalink_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
4483006ec67SBrian Somers {
4493006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4503006ec67SBrian Somers 
4513006ec67SBrian Somers   switch (dl->state) {
4523006ec67SBrian Somers     case DATALINK_CLOSED:
453c7cc5030SBrian Somers     case DATALINK_OPENING:
4543006ec67SBrian Somers       break;
455c7cc5030SBrian Somers 
4563006ec67SBrian Somers     case DATALINK_HANGUP:
4573006ec67SBrian Somers     case DATALINK_DIAL:
458c116e0c0SBrian Somers     case DATALINK_LOGOUT:
4593006ec67SBrian Somers     case DATALINK_LOGIN:
4603006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
4613006ec67SBrian Somers       break;
462c7cc5030SBrian Somers 
463c7cc5030SBrian Somers     case DATALINK_READY:
464e2ebb036SBrian Somers     case DATALINK_LCP:
465e2ebb036SBrian Somers     case DATALINK_AUTH:
46692b09558SBrian Somers     case DATALINK_CBCP:
4673006ec67SBrian Somers     case DATALINK_OPEN:
46858330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
46958330d7bSBrian Somers         descriptor_Read(&dl->chap.desc, bundle, fdset);
47058330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
4713006ec67SBrian Somers         descriptor_Read(&dl->physical->desc, bundle, fdset);
4723006ec67SBrian Somers       break;
4733006ec67SBrian Somers   }
4743006ec67SBrian Somers }
4753006ec67SBrian Somers 
4761af29a6eSBrian Somers static int
477f013f33eSBrian Somers datalink_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
4783006ec67SBrian Somers {
4793006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4801af29a6eSBrian Somers   int result = 0;
4813006ec67SBrian Somers 
4823006ec67SBrian Somers   switch (dl->state) {
4833006ec67SBrian Somers     case DATALINK_CLOSED:
484c7cc5030SBrian Somers     case DATALINK_OPENING:
4853006ec67SBrian Somers       break;
486c7cc5030SBrian Somers 
4873006ec67SBrian Somers     case DATALINK_HANGUP:
4883006ec67SBrian Somers     case DATALINK_DIAL:
489c116e0c0SBrian Somers     case DATALINK_LOGOUT:
4903006ec67SBrian Somers     case DATALINK_LOGIN:
4911af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4923006ec67SBrian Somers       break;
493c7cc5030SBrian Somers 
494c7cc5030SBrian Somers     case DATALINK_READY:
495e2ebb036SBrian Somers     case DATALINK_LCP:
496e2ebb036SBrian Somers     case DATALINK_AUTH:
49792b09558SBrian Somers     case DATALINK_CBCP:
4983006ec67SBrian Somers     case DATALINK_OPEN:
49958330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
50058330d7bSBrian Somers         result += descriptor_Write(&dl->chap.desc, bundle, fdset);
50158330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
50258330d7bSBrian Somers         result += descriptor_Write(&dl->physical->desc, bundle, fdset);
5033006ec67SBrian Somers       break;
5043006ec67SBrian Somers   }
5051af29a6eSBrian Somers 
5061af29a6eSBrian Somers   return result;
5073006ec67SBrian Somers }
5083006ec67SBrian Somers 
5096d666775SBrian Somers static void
5109c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
511e2ebb036SBrian Somers {
5129c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
513c11e57a3SBrian Somers     dl->dial.tries = -1;
514e2ebb036SBrian Somers     dl->reconnect_tries = 0;
5157729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
5169c81b87dSBrian Somers       dl->stayonline = 1;
517e2ebb036SBrian Somers   }
518e2ebb036SBrian Somers 
5197729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
5209c81b87dSBrian Somers     dl->stayonline = 0;
5216815097bSBrian Somers     physical_StopDeviceTimer(dl->physical);
5229c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
5239c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
5245d9e6103SBrian Somers     physical_Offline(dl->physical);
525e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
526c116e0c0SBrian Somers       if (dl->state == DATALINK_LOGOUT) {
5279ae58882SBrian Somers         datalink_NewState(dl, DATALINK_HANGUP);
528c39aa54eSBrian Somers         if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
529c39aa54eSBrian Somers           log_Printf(LogWARN, "Invalid hangup script\n");
530c116e0c0SBrian Somers       } else {
531c116e0c0SBrian Somers         datalink_NewState(dl, DATALINK_LOGOUT);
532c39aa54eSBrian Somers         if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
533c39aa54eSBrian Somers           log_Printf(LogWARN, "Invalid logout script\n");
534c116e0c0SBrian Somers       }
535e2ebb036SBrian Somers     } else
536e2ebb036SBrian Somers       datalink_HangupDone(dl);
537e2ebb036SBrian Somers   }
538e2ebb036SBrian Somers }
539e2ebb036SBrian Somers 
540e2ebb036SBrian Somers static void
5416d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
5426d666775SBrian Somers {
5436d666775SBrian Somers   /* The given FSM is about to start up ! */
5446d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
545a611cad6SBrian Somers 
54692e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
547e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
548e2ebb036SBrian Somers }
5496d666775SBrian Somers 
5506d666775SBrian Somers static void
5516d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
5526d666775SBrian Somers {
5536d666775SBrian Somers   /* The given fsm is now up */
5546d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
5555e315498SBrian Somers   struct lcp *lcp = &dl->physical->link.lcp;
5566d666775SBrian Somers 
55792e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
558f0cdd9c0SBrian Somers     datalink_GotAuthname(dl, "");
5595e315498SBrian Somers     lcp->auth_ineed = lcp->want_auth;
5605e315498SBrian Somers     lcp->auth_iwait = lcp->his_auth;
5615e315498SBrian Somers     if (lcp->his_auth || lcp->want_auth) {
5625945a079SBrian Somers       if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
5635563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
564dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
5655e315498SBrian Somers                 Auth2Nam(lcp->his_auth, lcp->his_authtype),
5665e315498SBrian Somers                 Auth2Nam(lcp->want_auth, lcp->want_authtype));
5675e315498SBrian Somers       if (lcp->his_auth == PROTO_PAP)
568f0cdd9c0SBrian Somers         auth_StartReq(&dl->pap);
5695e315498SBrian Somers       if (lcp->want_auth == PROTO_CHAP)
570f0cdd9c0SBrian Somers         auth_StartReq(&dl->chap.auth);
571e2ebb036SBrian Somers     } else
572e2ebb036SBrian Somers       datalink_AuthOk(dl);
573e2ebb036SBrian Somers   }
574e2ebb036SBrian Somers }
575e2ebb036SBrian Somers 
57664cfdfc6SBrian Somers static void
57764cfdfc6SBrian Somers datalink_AuthReInit(struct datalink *dl)
57864cfdfc6SBrian Somers {
57964cfdfc6SBrian Somers   auth_StopTimer(&dl->pap);
58064cfdfc6SBrian Somers   auth_StopTimer(&dl->chap.auth);
58164cfdfc6SBrian Somers   chap_ReInit(&dl->chap);
58264cfdfc6SBrian Somers }
58364cfdfc6SBrian Somers 
584e2ebb036SBrian Somers void
585f0cdd9c0SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name)
586643f4904SBrian Somers {
587f0cdd9c0SBrian Somers   strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
588f0cdd9c0SBrian Somers   dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
589643f4904SBrian Somers }
590643f4904SBrian Somers 
591643f4904SBrian Somers void
59292b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
593e2ebb036SBrian Somers {
59406337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5951df0a3b9SBrian Somers 
596dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5971fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5981fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5991fa665f5SBrian Somers       case MP_LINKSENT:
600ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
6011fa665f5SBrian Somers         return;
6021fa665f5SBrian Somers       case MP_UP:
6030a1b5c9dSBrian Somers         /* First link in the bundle */
60492b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
605ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
6060a1b5c9dSBrian Somers         /* fall through */
6071fa665f5SBrian Somers       case MP_ADDED:
6080a1b5c9dSBrian Somers         /* We're in multilink mode ! */
6091df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
610ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
6111fa665f5SBrian Somers         break;
6121fa665f5SBrian Somers       case MP_FAILED:
61349052c95SBrian Somers         datalink_AuthNotOk(dl);
61449052c95SBrian Somers         return;
61549052c95SBrian Somers     }
61649052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
617dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
618897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
619ab2de065SBrian Somers     bundle_CalculateBandwidth(dl->bundle);
620897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
62149052c95SBrian Somers     return;
62250abd4c8SBrian Somers   } else {
62350abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
624ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
62592b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
62650abd4c8SBrian Somers   }
62749052c95SBrian Somers 
62806337856SBrian Somers   if (ccpok) {
629dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
630dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
63106337856SBrian Somers   }
6329ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
6333b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
6343b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
6356d666775SBrian Somers }
636e2ebb036SBrian Somers 
637e2ebb036SBrian Somers void
63892b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
63992b09558SBrian Somers {
64092b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
64164cfdfc6SBrian Somers   datalink_AuthReInit(dl);
64292b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
64392b09558SBrian Somers }
64492b09558SBrian Somers 
64592b09558SBrian Somers void
64692b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
64792b09558SBrian Somers {
64892b09558SBrian Somers   cbcp_Down(&dl->cbcp);
64992b09558SBrian Somers   datalink_CBCPComplete(dl);
65092b09558SBrian Somers }
65192b09558SBrian Somers 
65292b09558SBrian Somers void
65392b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
65492b09558SBrian Somers {
6555165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
65692b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
6575165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
6585165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
6595165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
6605165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
6615165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
66292b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
66392b09558SBrian Somers     cbcp_Up(&dl->cbcp);
66492b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
6655165af6fSBrian Somers     /* It's not CBCP */
66692b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
66792b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
66864cfdfc6SBrian Somers     datalink_AuthReInit(dl);
66992b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
67092b09558SBrian Somers   } else
67192b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
67292b09558SBrian Somers       case 0:
67392b09558SBrian Somers         datalink_NCPUp(dl);
67492b09558SBrian Somers         break;
67592b09558SBrian Somers 
67692b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
67792b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
67892b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
67992b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
68092b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
68192b09558SBrian Somers                      dl->peer.authname);
68292b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
68392b09558SBrian Somers         } else {
68492b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
68592b09558SBrian Somers           if (ptr)
68692b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
68792b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
68892b09558SBrian Somers                      dl->cbcp.fsm.phone);
68992b09558SBrian Somers           dl->cbcp.required = 1;
69092b09558SBrian Somers         }
69192b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
69292b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
69364cfdfc6SBrian Somers         datalink_AuthReInit(dl);
69492b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
69592b09558SBrian Somers         break;
69692b09558SBrian Somers 
69792b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
69892b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
69992b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
70092b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
70192b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
70292b09558SBrian Somers                    dl->cbcp.fsm.phone);
70392b09558SBrian Somers         dl->cbcp.required = 1;
70492b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
70592b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
70664cfdfc6SBrian Somers         datalink_AuthReInit(dl);
70792b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
70892b09558SBrian Somers         break;
70992b09558SBrian Somers 
71092b09558SBrian Somers       default:
71192b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
71292b09558SBrian Somers                    dl->name);
71392b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
71464cfdfc6SBrian Somers         datalink_AuthReInit(dl);
71592b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
71692b09558SBrian Somers         break;
71792b09558SBrian Somers     }
71892b09558SBrian Somers }
71992b09558SBrian Somers 
72092b09558SBrian Somers void
721e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
722e2ebb036SBrian Somers {
7239ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
72464cfdfc6SBrian Somers   datalink_AuthReInit(dl);
725dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
7266d666775SBrian Somers }
7276d666775SBrian Somers 
7286d666775SBrian Somers static void
7296d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
7306d666775SBrian Somers {
7316d666775SBrian Somers   /* The given FSM has been told to come down */
7326d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
733a611cad6SBrian Somers 
73492e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
735e2ebb036SBrian Somers     switch (dl->state) {
736e2ebb036SBrian Somers       case DATALINK_OPEN:
737643f4904SBrian Somers         peerid_Init(&dl->peer);
73809206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
739ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
740e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
74192b09558SBrian Somers         /* fall through (just in case) */
74292b09558SBrian Somers 
74392b09558SBrian Somers       case DATALINK_CBCP:
74492b09558SBrian Somers         if (!dl->cbcp.required)
74592b09558SBrian Somers           cbcp_Down(&dl->cbcp);
74692b09558SBrian Somers         /* fall through (just in case) */
747e2ebb036SBrian Somers 
748e2ebb036SBrian Somers       case DATALINK_AUTH:
749dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
750dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
7516d666775SBrian Somers     }
7529ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
75364cfdfc6SBrian Somers     datalink_AuthReInit(dl);
754e2ebb036SBrian Somers   }
7556d666775SBrian Somers }
7566d666775SBrian Somers 
7576d666775SBrian Somers static void
7586d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
7596d666775SBrian Somers {
7606d666775SBrian Somers   /* The given fsm is now down */
7616d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
7626d666775SBrian Somers 
76392e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
76409206a6fSBrian Somers     fsm2initial(fp);
7656d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
7669c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
7670a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
7680a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
7696d666775SBrian Somers }
7706d666775SBrian Somers 
7713006ec67SBrian Somers struct datalink *
7726f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
7733006ec67SBrian Somers {
7743006ec67SBrian Somers   struct datalink *dl;
7753006ec67SBrian Somers 
7763006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
7773006ec67SBrian Somers   if (dl == NULL)
7783006ec67SBrian Somers     return dl;
7793006ec67SBrian Somers 
7803006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
7813006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
7823006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
7833006ec67SBrian Somers   dl->desc.Read = datalink_Read;
7843006ec67SBrian Somers   dl->desc.Write = datalink_Write;
7855b8b8060SBrian Somers 
786e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
787e718d1d7SBrian Somers 
78873a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
78973a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
790c116e0c0SBrian Somers   *dl->cfg.script.logout = '\0';
79173a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
792f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
793f9545805SBrian Somers   *dl->phone.list = '\0';
794f9545805SBrian Somers   dl->phone.next = NULL;
795f9545805SBrian Somers   dl->phone.alt = NULL;
796f9545805SBrian Somers   dl->phone.chosen = "N/A";
7979c81b87dSBrian Somers   dl->stayonline = 0;
798c7cc5030SBrian Somers   dl->script.run = 1;
799c7cc5030SBrian Somers   dl->script.packetmode = 1;
8003b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
8015b8b8060SBrian Somers 
8023006ec67SBrian Somers   dl->bundle = bundle;
8033006ec67SBrian Somers   dl->next = NULL;
804e718d1d7SBrian Somers 
805c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
806e718d1d7SBrian Somers 
807c11e57a3SBrian Somers   dl->dial.tries = 0;
808565e35e5SBrian Somers   dl->cfg.dial.max = 1;
809565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
810565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
811c11e57a3SBrian Somers   dl->cfg.dial.inc = 0;
812c11e57a3SBrian Somers   dl->cfg.dial.maxinc = 10;
813e718d1d7SBrian Somers 
814abff9baeSBrian Somers   dl->reconnect_tries = 0;
815565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
816565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
817abff9baeSBrian Somers 
81892b09558SBrian Somers   dl->cfg.callback.opmask = 0;
81992b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
82092b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
82192b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
82292b09558SBrian Somers 
8233006ec67SBrian Somers   dl->name = strdup(name);
824643f4904SBrian Somers   peerid_Init(&dl->peer);
8256f384573SBrian Somers   dl->parent = &bundle->fsm;
8263b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
8273b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
8283b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
8293b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
8303b0f8d2eSBrian Somers   dl->fsmp.object = dl;
8313b0f8d2eSBrian Somers 
8325d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, type)) == NULL) {
8333006ec67SBrian Somers     free(dl->name);
8343006ec67SBrian Somers     free(dl);
8353006ec67SBrian Somers     return NULL;
8363006ec67SBrian Somers   }
837f0cdd9c0SBrian Somers 
838f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
839f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
84092b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
841c116e0c0SBrian Somers 
842c116e0c0SBrian Somers   memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
843ffcfaec7SBrian Somers   chat_Init(&dl->chat, dl->physical);
8443006ec67SBrian Somers 
8459ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
8469ae58882SBrian Somers              dl->name, datalink_State(dl));
8473006ec67SBrian Somers 
8483006ec67SBrian Somers   return dl;
8493006ec67SBrian Somers }
8503006ec67SBrian Somers 
8513006ec67SBrian Somers struct datalink *
852cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
853cd7bd93aSBrian Somers {
854cd7bd93aSBrian Somers   struct datalink *dl;
855cd7bd93aSBrian Somers 
856cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
857cd7bd93aSBrian Somers   if (dl == NULL)
858cd7bd93aSBrian Somers     return dl;
859cd7bd93aSBrian Somers 
860cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
861cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
862cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
863cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
864cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
865cd7bd93aSBrian Somers 
866cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
867cd7bd93aSBrian Somers 
868cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
869cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
870cd7bd93aSBrian Somers   *dl->phone.list = '\0';
871643f4904SBrian Somers   dl->phone.next = NULL;
872643f4904SBrian Somers   dl->phone.alt = NULL;
873643f4904SBrian Somers   dl->phone.chosen = "N/A";
874cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
875cd7bd93aSBrian Somers   dl->next = NULL;
876c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
877c11e57a3SBrian Somers   dl->dial.tries = 0;
878cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
879cd7bd93aSBrian Somers   dl->name = strdup(name);
880643f4904SBrian Somers   peerid_Init(&dl->peer);
881cd7bd93aSBrian Somers   dl->parent = odl->parent;
882cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
883643f4904SBrian Somers   dl->fsmp.object = dl;
884cd7bd93aSBrian Somers 
8855d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
886cd7bd93aSBrian Somers     free(dl->name);
887cd7bd93aSBrian Somers     free(dl);
888cd7bd93aSBrian Somers     return NULL;
889cd7bd93aSBrian Somers   }
890f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
891479508cfSBrian Somers   dl->pap.cfg = odl->pap.cfg;
892f0cdd9c0SBrian Somers 
893f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
894479508cfSBrian Somers   dl->chap.auth.cfg = odl->chap.auth.cfg;
895f0cdd9c0SBrian Somers 
896cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
897cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
898cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
899cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
900cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
901cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
902cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
903cd7bd93aSBrian Somers 
90492b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
905c116e0c0SBrian Somers 
906c116e0c0SBrian Somers   memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
907ffcfaec7SBrian Somers   chat_Init(&dl->chat, dl->physical);
908cd7bd93aSBrian Somers 
9099ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
9109ae58882SBrian Somers              dl->name, datalink_State(dl));
911cd7bd93aSBrian Somers 
912cd7bd93aSBrian Somers   return dl;
913cd7bd93aSBrian Somers }
914cd7bd93aSBrian Somers 
915cd7bd93aSBrian Somers struct datalink *
9163006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
9173006ec67SBrian Somers {
9183006ec67SBrian Somers   struct datalink *result;
9193006ec67SBrian Somers 
92039d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
921dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
922c7cc5030SBrian Somers               datalink_State(dl));
92339d94652SBrian Somers     switch (dl->state) {
92439d94652SBrian Somers       case DATALINK_HANGUP:
92539d94652SBrian Somers       case DATALINK_DIAL:
92639d94652SBrian Somers       case DATALINK_LOGIN:
927ffcfaec7SBrian Somers         chat_Finish(&dl->chat);		/* Gotta blat the timers ! */
92839d94652SBrian Somers         break;
92939d94652SBrian Somers     }
93039d94652SBrian Somers   }
9313006ec67SBrian Somers 
932ffcfaec7SBrian Somers   chat_Destroy(&dl->chat);
933c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
9343006ec67SBrian Somers   result = dl->next;
9355d9e6103SBrian Somers   physical_Destroy(dl->physical);
9363006ec67SBrian Somers   free(dl->name);
9373006ec67SBrian Somers   free(dl);
9383006ec67SBrian Somers 
9393006ec67SBrian Somers   return result;
9403006ec67SBrian Somers }
9413006ec67SBrian Somers 
9423006ec67SBrian Somers void
943c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
9443006ec67SBrian Somers {
9456f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
946565e35e5SBrian Somers     /* Ignore scripts */
947565e35e5SBrian Somers     runscripts = 0;
948565e35e5SBrian Somers 
949c7cc5030SBrian Somers   switch (dl->state) {
950c7cc5030SBrian Somers     case DATALINK_CLOSED:
9513b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
9523b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
9533b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
9549ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
955565e35e5SBrian Somers       dl->reconnect_tries =
9566f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
957c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
958c5a5a6caSBrian Somers       dl->script.run = runscripts;
959c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
960c7cc5030SBrian Somers       break;
961c7cc5030SBrian Somers 
962c7cc5030SBrian Somers     case DATALINK_OPENING:
963c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
964c7cc5030SBrian Somers         dl->script.run = 1;
965c7cc5030SBrian Somers       /* fall through */
966c7cc5030SBrian Somers 
967c7cc5030SBrian Somers     case DATALINK_DIAL:
968c7cc5030SBrian Somers     case DATALINK_LOGIN:
969c7cc5030SBrian Somers     case DATALINK_READY:
970c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
971c7cc5030SBrian Somers         dl->script.packetmode = 1;
972c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
973c7cc5030SBrian Somers           datalink_LoginDone(dl);
974c7cc5030SBrian Somers       }
975c7cc5030SBrian Somers       break;
9763006ec67SBrian Somers   }
9773006ec67SBrian Somers }
9783006ec67SBrian Somers 
9793006ec67SBrian Somers void
9809c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
981c7cc5030SBrian Somers {
982c7cc5030SBrian Somers   /* Please close */
983e2ebb036SBrian Somers   switch (dl->state) {
984e2ebb036SBrian Somers     case DATALINK_OPEN:
985643f4904SBrian Somers       peerid_Init(&dl->peer);
98609206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
987e2ebb036SBrian Somers       /* fall through */
988e2ebb036SBrian Somers 
98992b09558SBrian Somers     case DATALINK_CBCP:
990e2ebb036SBrian Somers     case DATALINK_AUTH:
991e2ebb036SBrian Somers     case DATALINK_LCP:
99264cfdfc6SBrian Somers       datalink_AuthReInit(dl);
993dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
9949c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
995c11e57a3SBrian Somers         dl->dial.tries = -1;
996c7cc5030SBrian Somers         dl->reconnect_tries = 0;
9979c81b87dSBrian Somers         if (how == CLOSE_LCP)
9989c81b87dSBrian Somers           dl->stayonline = 1;
999c7cc5030SBrian Somers       }
1000d81ecf9aSBrian Somers       break;
1001e2ebb036SBrian Somers 
1002e2ebb036SBrian Somers     default:
10039c81b87dSBrian Somers       datalink_ComeDown(dl, how);
1004c7cc5030SBrian Somers   }
1005e2ebb036SBrian Somers }
1006c7cc5030SBrian Somers 
1007c7cc5030SBrian Somers void
10089c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
1009c7cc5030SBrian Somers {
1010c7cc5030SBrian Somers   /* Carrier is lost */
1011e2ebb036SBrian Somers   switch (dl->state) {
1012e2ebb036SBrian Somers     case DATALINK_OPEN:
1013643f4904SBrian Somers       peerid_Init(&dl->peer);
101409206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
1015e2ebb036SBrian Somers       /* fall through */
1016e2ebb036SBrian Somers 
101792b09558SBrian Somers     case DATALINK_CBCP:
1018e2ebb036SBrian Somers     case DATALINK_AUTH:
1019e2ebb036SBrian Somers     case DATALINK_LCP:
102009206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
1021225e259dSBrian Somers       if (dl->state == DATALINK_OPENING)
1022225e259dSBrian Somers         return;			/* we're doing a callback... */
1023e2ebb036SBrian Somers       /* fall through */
1024c7cc5030SBrian Somers 
1025e2ebb036SBrian Somers     default:
10269c81b87dSBrian Somers       datalink_ComeDown(dl, how);
1027c7cc5030SBrian Somers   }
1028e2ebb036SBrian Somers }
1029c7cc5030SBrian Somers 
1030c7cc5030SBrian Somers void
10313006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
10323006ec67SBrian Somers {
10333006ec67SBrian Somers   dl->reconnect_tries = 0;
10343006ec67SBrian Somers }
1035c7cc5030SBrian Somers 
10369c81b87dSBrian Somers void
10379c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
10389c81b87dSBrian Somers {
10397729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
10409c81b87dSBrian Somers     dl->stayonline = 1;
10419c81b87dSBrian Somers }
10429c81b87dSBrian Somers 
1043643f4904SBrian Somers int
1044643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
1045c7cc5030SBrian Somers {
1046643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
1047643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
1048643f4904SBrian Somers                 datalink_State(arg->cx));
1049643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
1050643f4904SBrian Somers   if (*arg->cx->peer.authname)
1051643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
1052643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
1053643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
1054565e35e5SBrian Somers   else
1055643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
1056643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
1057643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
1058643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
1059643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
1060643f4904SBrian Somers 
1061643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
1062643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
1063643f4904SBrian Somers                 arg->cx->cfg.phone.list);
1064643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
1065643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
1066643f4904SBrian Somers                   arg->cx->cfg.dial.max);
1067565e35e5SBrian Somers   else
1068643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
1069b5c347a3SBrian Somers   if (arg->cx->cfg.dial.next_timeout >= 0)
1070643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
1071565e35e5SBrian Somers   else
1072643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
1073b5c347a3SBrian Somers   if (arg->cx->cfg.dial.timeout >= 0)
1074643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
1075565e35e5SBrian Somers   else
1076643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
1077643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
1078643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
1079643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
1080643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
1081643f4904SBrian Somers   else
1082643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
108392b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
108492b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
108592b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
108692b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
108792b09558SBrian Somers   else {
108892b09558SBrian Somers     int comma = 0;
108992b09558SBrian Somers 
109092b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
109192b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
109292b09558SBrian Somers       comma = 1;
109392b09558SBrian Somers     }
109492b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
109592b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
109692b09558SBrian Somers       comma = 1;
109792b09558SBrian Somers     }
109892b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
109992b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
110092b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
110192b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
110292b09558SBrian Somers       comma = 1;
110392b09558SBrian Somers     }
110492b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
110592b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
110692b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
110792b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
1108cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
1109cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
1110cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
1111cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
1112cf784a89SBrian Somers         else
1113cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
1114cf784a89SBrian Somers       } else
1115cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
111692b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
111792b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
111892b09558SBrian Somers     } else
111992b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
112092b09558SBrian Somers   }
112192b09558SBrian Somers 
1122643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1123643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1124643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1125643f4904SBrian Somers                 arg->cx->cfg.script.login);
1126c116e0c0SBrian Somers   prompt_Printf(arg->prompt, " Logout Script:      %s\n",
1127c116e0c0SBrian Somers                 arg->cx->cfg.script.logout);
1128643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1129643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1130643f4904SBrian Somers   return 0;
1131565e35e5SBrian Somers }
1132565e35e5SBrian Somers 
1133565e35e5SBrian Somers int
1134565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1135565e35e5SBrian Somers {
113625092092SBrian Somers   if (arg->argc == arg->argn+2) {
113725092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
113825092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1139565e35e5SBrian Somers     return 0;
1140565e35e5SBrian Somers   }
1141565e35e5SBrian Somers   return -1;
1142565e35e5SBrian Somers }
1143565e35e5SBrian Somers 
1144565e35e5SBrian Somers int
1145565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1146565e35e5SBrian Somers {
1147c11e57a3SBrian Somers   const char *sep, *osep;
1148c11e57a3SBrian Somers   int timeout, inc, maxinc, tries;
1149565e35e5SBrian Somers 
115025092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
115125092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
115225092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1153565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1154565e35e5SBrian Somers       randinit();
1155565e35e5SBrian Somers     } else {
115625092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1157565e35e5SBrian Somers 
1158565e35e5SBrian Somers       if (timeout >= 0)
1159565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1160565e35e5SBrian Somers       else {
1161dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1162565e35e5SBrian Somers 	return -1;
1163565e35e5SBrian Somers       }
1164565e35e5SBrian Somers     }
1165565e35e5SBrian Somers 
1166c11e57a3SBrian Somers     sep = strchr(arg->argv[arg->argn], '+');
1167c11e57a3SBrian Somers     if (sep) {
1168c11e57a3SBrian Somers       inc = atoi(++sep);
1169c11e57a3SBrian Somers       osep = sep;
1170c11e57a3SBrian Somers       if (inc >= 0)
1171c11e57a3SBrian Somers         arg->cx->cfg.dial.inc = inc;
1172c11e57a3SBrian Somers       else {
1173c11e57a3SBrian Somers         log_Printf(LogWARN, "Invalid timeout increment\n");
1174c11e57a3SBrian Somers         return -1;
1175c11e57a3SBrian Somers       }
1176c11e57a3SBrian Somers       sep = strchr(sep, '-');
1177c11e57a3SBrian Somers       if (sep) {
1178c11e57a3SBrian Somers         maxinc = atoi(++sep);
1179c11e57a3SBrian Somers         if (maxinc >= 0)
1180c11e57a3SBrian Somers           arg->cx->cfg.dial.maxinc = maxinc;
1181c11e57a3SBrian Somers         else {
1182c11e57a3SBrian Somers           log_Printf(LogWARN, "Invalid maximum timeout increments\n");
1183c11e57a3SBrian Somers           return -1;
1184c11e57a3SBrian Somers         }
1185c11e57a3SBrian Somers       } else {
1186c11e57a3SBrian Somers         /* Default timeout increment */
1187c11e57a3SBrian Somers         arg->cx->cfg.dial.maxinc = 10;
1188c11e57a3SBrian Somers         sep = osep;
1189c11e57a3SBrian Somers       }
1190c11e57a3SBrian Somers     } else {
1191c11e57a3SBrian Somers       /* Default timeout increment & max increment */
1192c11e57a3SBrian Somers       arg->cx->cfg.dial.inc = 0;
1193c11e57a3SBrian Somers       arg->cx->cfg.dial.maxinc = 10;
1194c11e57a3SBrian Somers       sep = arg->argv[arg->argn];
1195c11e57a3SBrian Somers     }
1196c11e57a3SBrian Somers 
1197c11e57a3SBrian Somers     sep = strchr(sep, '.');
1198c11e57a3SBrian Somers     if (sep) {
1199c11e57a3SBrian Somers       if (strcasecmp(++sep, "random") == 0) {
1200565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1201565e35e5SBrian Somers 	randinit();
1202565e35e5SBrian Somers       } else {
1203c11e57a3SBrian Somers 	timeout = atoi(sep);
1204565e35e5SBrian Somers 	if (timeout >= 0)
1205565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1206565e35e5SBrian Somers 	else {
1207dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1208565e35e5SBrian Somers 	  return -1;
1209565e35e5SBrian Somers 	}
1210565e35e5SBrian Somers       }
1211565e35e5SBrian Somers     } else
1212565e35e5SBrian Somers       /* Default next timeout */
1213565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1214565e35e5SBrian Somers 
121525092092SBrian Somers     if (arg->argc == arg->argn+2) {
121625092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1217565e35e5SBrian Somers 
1218565e35e5SBrian Somers       if (tries >= 0) {
1219565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1220565e35e5SBrian Somers       } else {
1221dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1222565e35e5SBrian Somers 	return 1;
1223565e35e5SBrian Somers       }
1224565e35e5SBrian Somers     }
1225565e35e5SBrian Somers     return 0;
1226565e35e5SBrian Somers   }
1227c11e57a3SBrian Somers 
1228565e35e5SBrian Somers   return -1;
1229c7cc5030SBrian Somers }
1230c7cc5030SBrian Somers 
1231182c898aSBrian Somers static const char * const states[] = {
1232565e35e5SBrian Somers   "closed",
1233565e35e5SBrian Somers   "opening",
1234565e35e5SBrian Somers   "hangup",
1235565e35e5SBrian Somers   "dial",
1236eb6e5e05SBrian Somers   "carrier",
1237c116e0c0SBrian Somers   "logout",
1238565e35e5SBrian Somers   "login",
1239565e35e5SBrian Somers   "ready",
1240565e35e5SBrian Somers   "lcp",
1241565e35e5SBrian Somers   "auth",
124292b09558SBrian Somers   "cbcp",
1243565e35e5SBrian Somers   "open"
1244c7cc5030SBrian Somers };
1245c7cc5030SBrian Somers 
1246643f4904SBrian Somers const char *
1247c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1248c7cc5030SBrian Somers {
1249c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1250c7cc5030SBrian Somers     return "unknown";
1251c7cc5030SBrian Somers   return states[dl->state];
1252c7cc5030SBrian Somers }
12536f384573SBrian Somers 
12549ae58882SBrian Somers static void
12559ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
12569ae58882SBrian Somers {
12579ae58882SBrian Somers   if (state != dl->state) {
12589ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
12599ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
12609ae58882SBrian Somers                  states[state]);
12619ae58882SBrian Somers       dl->state = state;
12629ae58882SBrian Somers     } else
12639ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
12649ae58882SBrian Somers   }
12659ae58882SBrian Somers }
12669ae58882SBrian Somers 
12676f384573SBrian Somers struct datalink *
126896c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
126987c3786eSBrian Somers              int fd, int *auxfd, int *nauxfd)
12706f384573SBrian Somers {
1271b7c5748eSBrian Somers   struct datalink *dl, *cdl;
1272479508cfSBrian Somers   struct fsm_retry copy;
1273b7c5748eSBrian Somers   char *oname;
12746f384573SBrian Somers 
127596c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
127696c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
12776f384573SBrian Somers 
127896c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
127996c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
128096c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
128196c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
12826f384573SBrian Somers   }
1283b7c5748eSBrian Somers 
1284b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1285b7c5748eSBrian Somers   oname = NULL;
1286b7c5748eSBrian Somers   do {
1287b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1288b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1289b7c5748eSBrian Somers         if (oname)
1290b7c5748eSBrian Somers           free(datalink_NextName(dl));
1291b7c5748eSBrian Somers         else
1292b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1293b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1294b7c5748eSBrian Somers       }
1295b7c5748eSBrian Somers   } while (cdl);
1296b7c5748eSBrian Somers 
1297b7c5748eSBrian Somers   if (oname) {
1298b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1299b7c5748eSBrian Somers     free(oname);
1300b7c5748eSBrian Somers   } else {
130196c9bb21SBrian Somers     dl->name = strdup(dl->name);
1302b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1303b7c5748eSBrian Somers   }
1304b7c5748eSBrian Somers   (*niov)++;
13056f384573SBrian Somers 
13066f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
13076f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
13086f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
13096f384573SBrian Somers   dl->desc.Read = datalink_Read;
13106f384573SBrian Somers   dl->desc.Write = datalink_Write;
13116f384573SBrian Somers 
13126f384573SBrian Somers   mp_linkInit(&dl->mp);
13136f384573SBrian Somers   *dl->phone.list = '\0';
13146f384573SBrian Somers   dl->phone.next = NULL;
13156f384573SBrian Somers   dl->phone.alt = NULL;
13166f384573SBrian Somers   dl->phone.chosen = "N/A";
13176f384573SBrian Somers 
13186f384573SBrian Somers   dl->bundle = bundle;
13196f384573SBrian Somers   dl->next = NULL;
1320c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
1321c11e57a3SBrian Somers   dl->dial.tries = 0;
13226f384573SBrian Somers   dl->reconnect_tries = 0;
13236f384573SBrian Somers   dl->parent = &bundle->fsm;
13246f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
13256f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
13266f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
13276f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
13286f384573SBrian Somers   dl->fsmp.object = dl;
13296f384573SBrian Somers 
133087c3786eSBrian Somers   dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
133196c9bb21SBrian Somers 
133296c9bb21SBrian Somers   if (!dl->physical) {
13336f384573SBrian Somers     free(dl->name);
13346f384573SBrian Somers     free(dl);
13356f384573SBrian Somers     dl = NULL;
13369ae58882SBrian Somers   } else {
1337479508cfSBrian Somers     copy = dl->pap.cfg.fsm;
1338f0cdd9c0SBrian Somers     pap_Init(&dl->pap, dl->physical);
1339479508cfSBrian Somers     dl->pap.cfg.fsm = copy;
1340f0cdd9c0SBrian Somers 
1341479508cfSBrian Somers     copy = dl->chap.auth.cfg.fsm;
1342f0cdd9c0SBrian Somers     chap_Init(&dl->chap, dl->physical);
1343479508cfSBrian Somers     dl->chap.auth.cfg.fsm = copy;
1344f0cdd9c0SBrian Somers 
134592b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
1346c116e0c0SBrian Somers 
1347c116e0c0SBrian Somers     memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
1348ffcfaec7SBrian Somers     chat_Init(&dl->chat, dl->physical);
13496f384573SBrian Somers 
13509ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
13519ae58882SBrian Somers               dl->name, datalink_State(dl));
13529ae58882SBrian Somers   }
13539ae58882SBrian Somers 
13546f384573SBrian Somers   return dl;
13556f384573SBrian Somers }
13566f384573SBrian Somers 
13576f384573SBrian Somers int
135885fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
13592cb305afSBrian Somers              int *auxfd, int *nauxfd)
13606f384573SBrian Somers {
136196c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
136296c9bb21SBrian Somers   int link_fd;
13636f384573SBrian Somers 
136496c9bb21SBrian Somers   if (dl) {
1365c11e57a3SBrian Somers     timer_Stop(&dl->dial.timer);
136692b09558SBrian Somers     /* The following is purely for the sake of paranoia */
136792b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1368dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1369dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
13706f384573SBrian Somers   }
13716f384573SBrian Somers 
137296c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
137396c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
137496c9bb21SBrian Somers     if (dl) {
13756f384573SBrian Somers       free(dl->name);
13766f384573SBrian Somers       free(dl);
137796c9bb21SBrian Somers     }
137896c9bb21SBrian Somers     return -1;
137996c9bb21SBrian Somers   }
138096c9bb21SBrian Somers 
13812cb305afSBrian Somers   iov[*niov].iov_base = (void *)dl;
138296c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
13832cb305afSBrian Somers   iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
138496c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
138596c9bb21SBrian Somers 
138687c3786eSBrian Somers   link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
13872cb305afSBrian Somers                          nauxfd);
138896c9bb21SBrian Somers 
138996c9bb21SBrian Somers   if (link_fd == -1 && dl) {
139096c9bb21SBrian Somers     free(dl->name);
139196c9bb21SBrian Somers     free(dl);
139296c9bb21SBrian Somers   }
13936f384573SBrian Somers 
13946f384573SBrian Somers   return link_fd;
13956f384573SBrian Somers }
13966f384573SBrian Somers 
139758d55334SBrian Somers void
139858d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
139958d55334SBrian Somers {
140058d55334SBrian Somers   free(dl->name);
140158d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
140258d55334SBrian Somers }
140358d55334SBrian Somers 
140484917b87SBrian Somers char *
140584917b87SBrian Somers datalink_NextName(struct datalink *dl)
14066f384573SBrian Somers {
14076f384573SBrian Somers   int f, n;
140884917b87SBrian Somers   char *name, *oname;
14096f384573SBrian Somers 
14106f384573SBrian Somers   n = strlen(dl->name);
14116f384573SBrian Somers   name = (char *)malloc(n+3);
14126f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
14136f384573SBrian Somers     if (!isdigit(dl->name[f]))
14146f384573SBrian Somers       break;
14156f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
14166f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
141784917b87SBrian Somers   oname = dl->name;
141854cd8e13SBrian Somers   dl->name = name;
141954cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
142084917b87SBrian Somers   return oname;
14216f384573SBrian Somers }
1422dd0645c5SBrian Somers 
1423dd0645c5SBrian Somers int
1424dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1425dd0645c5SBrian Somers {
1426dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1427dd0645c5SBrian Somers     return 0;
1428dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1429dd0645c5SBrian Somers     dl->script.run = 0;
1430dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1431dd0645c5SBrian Somers     dl->reconnect_tries = 0;
1432f6a4e748SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
1433f6a4e748SBrian Somers       dl->state <= DATALINK_READY)
1434dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1435dd0645c5SBrian Somers   return 1;
1436dd0645c5SBrian Somers }
1437c11e57a3SBrian Somers 
1438c11e57a3SBrian Somers int
1439c11e57a3SBrian Somers datalink_GetDialTimeout(struct datalink *dl)
1440c11e57a3SBrian Somers {
1441c11e57a3SBrian Somers   int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
1442c11e57a3SBrian Somers 
1443c11e57a3SBrian Somers   if (dl->dial.incs < dl->cfg.dial.maxinc)
1444c11e57a3SBrian Somers     dl->dial.incs++;
1445c11e57a3SBrian Somers 
1446c11e57a3SBrian Somers   return result;
1447c11e57a3SBrian Somers }
1448