xref: /freebsd/usr.sbin/ppp/datalink.c (revision 2fc2f705)
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 "descriptor.h"
49879ed6faSBrian Somers #include "lqr.h"
503006ec67SBrian Somers #include "hdlc.h"
511038894eSBrian Somers #include "lcp.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
4779b996792SBrian Somers datalink_Write(struct fdescriptor *d, struct bundle *bundle,
4789b996792SBrian Somers                const fd_set *fdset)
4793006ec67SBrian Somers {
4803006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4811af29a6eSBrian Somers   int result = 0;
4823006ec67SBrian Somers 
4833006ec67SBrian Somers   switch (dl->state) {
4843006ec67SBrian Somers     case DATALINK_CLOSED:
485c7cc5030SBrian Somers     case DATALINK_OPENING:
4863006ec67SBrian Somers       break;
487c7cc5030SBrian Somers 
4883006ec67SBrian Somers     case DATALINK_HANGUP:
4893006ec67SBrian Somers     case DATALINK_DIAL:
490c116e0c0SBrian Somers     case DATALINK_LOGOUT:
4913006ec67SBrian Somers     case DATALINK_LOGIN:
4921af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4933006ec67SBrian Somers       break;
494c7cc5030SBrian Somers 
495c7cc5030SBrian Somers     case DATALINK_READY:
496e2ebb036SBrian Somers     case DATALINK_LCP:
497e2ebb036SBrian Somers     case DATALINK_AUTH:
49892b09558SBrian Somers     case DATALINK_CBCP:
4993006ec67SBrian Somers     case DATALINK_OPEN:
50058330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
50158330d7bSBrian Somers         result += descriptor_Write(&dl->chap.desc, bundle, fdset);
50258330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
50358330d7bSBrian Somers         result += descriptor_Write(&dl->physical->desc, bundle, fdset);
5043006ec67SBrian Somers       break;
5053006ec67SBrian Somers   }
5061af29a6eSBrian Somers 
5071af29a6eSBrian Somers   return result;
5083006ec67SBrian Somers }
5093006ec67SBrian Somers 
5106d666775SBrian Somers static void
5119c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
512e2ebb036SBrian Somers {
5132fc2f705SBrian Somers   int stayonline;
514e2ebb036SBrian Somers 
5152fc2f705SBrian Somers   if (how == CLOSE_LCP)
5162fc2f705SBrian Somers     datalink_DontHangup(dl);
5172fc2f705SBrian Somers   else if (how == CLOSE_STAYDOWN)
5182fc2f705SBrian Somers     datalink_StayDown(dl);
5192fc2f705SBrian Somers 
5202fc2f705SBrian Somers   stayonline = dl->stayonline;
5219c81b87dSBrian Somers   dl->stayonline = 0;
5222fc2f705SBrian Somers 
5232fc2f705SBrian Somers   if (dl->state >= DATALINK_READY && stayonline) {
5246815097bSBrian Somers     physical_StopDeviceTimer(dl->physical);
5259c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
5269c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
5275d9e6103SBrian Somers     physical_Offline(dl->physical);
528e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
529c116e0c0SBrian Somers       if (dl->state == DATALINK_LOGOUT) {
5309ae58882SBrian Somers         datalink_NewState(dl, DATALINK_HANGUP);
531c39aa54eSBrian Somers         if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
532c39aa54eSBrian Somers           log_Printf(LogWARN, "Invalid hangup script\n");
533c116e0c0SBrian Somers       } else {
534c116e0c0SBrian Somers         datalink_NewState(dl, DATALINK_LOGOUT);
535c39aa54eSBrian Somers         if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
536c39aa54eSBrian Somers           log_Printf(LogWARN, "Invalid logout script\n");
537c116e0c0SBrian Somers       }
538e2ebb036SBrian Somers     } else
539e2ebb036SBrian Somers       datalink_HangupDone(dl);
540e2ebb036SBrian Somers   }
541e2ebb036SBrian Somers }
542e2ebb036SBrian Somers 
543e2ebb036SBrian Somers static void
5446d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
5456d666775SBrian Somers {
5466d666775SBrian Somers   /* The given FSM is about to start up ! */
5476d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
548a611cad6SBrian Somers 
54992e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
550e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
551e2ebb036SBrian Somers }
5526d666775SBrian Somers 
5536d666775SBrian Somers static void
5546d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
5556d666775SBrian Somers {
5566d666775SBrian Somers   /* The given fsm is now up */
5576d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
5585e315498SBrian Somers   struct lcp *lcp = &dl->physical->link.lcp;
5596d666775SBrian Somers 
56092e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
561f0cdd9c0SBrian Somers     datalink_GotAuthname(dl, "");
5625e315498SBrian Somers     lcp->auth_ineed = lcp->want_auth;
5635e315498SBrian Somers     lcp->auth_iwait = lcp->his_auth;
5645e315498SBrian Somers     if (lcp->his_auth || lcp->want_auth) {
5655945a079SBrian Somers       if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
5665563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
567dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
5685e315498SBrian Somers                 Auth2Nam(lcp->his_auth, lcp->his_authtype),
5695e315498SBrian Somers                 Auth2Nam(lcp->want_auth, lcp->want_authtype));
5705e315498SBrian Somers       if (lcp->his_auth == PROTO_PAP)
571f0cdd9c0SBrian Somers         auth_StartReq(&dl->pap);
5725e315498SBrian Somers       if (lcp->want_auth == PROTO_CHAP)
573f0cdd9c0SBrian Somers         auth_StartReq(&dl->chap.auth);
574e2ebb036SBrian Somers     } else
575e2ebb036SBrian Somers       datalink_AuthOk(dl);
576e2ebb036SBrian Somers   }
577e2ebb036SBrian Somers }
578e2ebb036SBrian Somers 
57964cfdfc6SBrian Somers static void
58064cfdfc6SBrian Somers datalink_AuthReInit(struct datalink *dl)
58164cfdfc6SBrian Somers {
58264cfdfc6SBrian Somers   auth_StopTimer(&dl->pap);
58364cfdfc6SBrian Somers   auth_StopTimer(&dl->chap.auth);
58464cfdfc6SBrian Somers   chap_ReInit(&dl->chap);
58564cfdfc6SBrian Somers }
58664cfdfc6SBrian Somers 
587e2ebb036SBrian Somers void
588f0cdd9c0SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name)
589643f4904SBrian Somers {
590f0cdd9c0SBrian Somers   strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
591f0cdd9c0SBrian Somers   dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
592643f4904SBrian Somers }
593643f4904SBrian Somers 
594643f4904SBrian Somers void
59592b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
596e2ebb036SBrian Somers {
59706337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5981df0a3b9SBrian Somers 
599dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
6001fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
6011fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
6021fa665f5SBrian Somers       case MP_LINKSENT:
603ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
6041fa665f5SBrian Somers         return;
6051fa665f5SBrian Somers       case MP_UP:
6060a1b5c9dSBrian Somers         /* First link in the bundle */
60792b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
608ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
6090a1b5c9dSBrian Somers         /* fall through */
6101fa665f5SBrian Somers       case MP_ADDED:
6110a1b5c9dSBrian Somers         /* We're in multilink mode ! */
6121df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
613ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
6141fa665f5SBrian Somers         break;
6151fa665f5SBrian Somers       case MP_FAILED:
61649052c95SBrian Somers         datalink_AuthNotOk(dl);
61749052c95SBrian Somers         return;
61849052c95SBrian Somers     }
61949052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
620dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
621897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
622ab2de065SBrian Somers     bundle_CalculateBandwidth(dl->bundle);
623897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
62449052c95SBrian Somers     return;
62550abd4c8SBrian Somers   } else {
62650abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
627ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
62892b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
62950abd4c8SBrian Somers   }
63049052c95SBrian Somers 
63106337856SBrian Somers   if (ccpok) {
632dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
633dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
63406337856SBrian Somers   }
6359ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
6363b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
6373b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
6386d666775SBrian Somers }
639e2ebb036SBrian Somers 
640e2ebb036SBrian Somers void
64192b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
64292b09558SBrian Somers {
64392b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
64464cfdfc6SBrian Somers   datalink_AuthReInit(dl);
64592b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
64692b09558SBrian Somers }
64792b09558SBrian Somers 
64892b09558SBrian Somers void
64992b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
65092b09558SBrian Somers {
65192b09558SBrian Somers   cbcp_Down(&dl->cbcp);
65292b09558SBrian Somers   datalink_CBCPComplete(dl);
65392b09558SBrian Somers }
65492b09558SBrian Somers 
65592b09558SBrian Somers void
65692b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
65792b09558SBrian Somers {
6585165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
65992b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
6605165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
6615165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
6625165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
6635165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
6645165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
66592b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
66692b09558SBrian Somers     cbcp_Up(&dl->cbcp);
66792b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
6685165af6fSBrian Somers     /* It's not CBCP */
66992b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
67092b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
67164cfdfc6SBrian Somers     datalink_AuthReInit(dl);
67292b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
67392b09558SBrian Somers   } else
67492b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
67592b09558SBrian Somers       case 0:
67692b09558SBrian Somers         datalink_NCPUp(dl);
67792b09558SBrian Somers         break;
67892b09558SBrian Somers 
67992b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
68092b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
68192b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
68292b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
68392b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
68492b09558SBrian Somers                      dl->peer.authname);
68592b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
68692b09558SBrian Somers         } else {
68792b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
68892b09558SBrian Somers           if (ptr)
68992b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
69092b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
69192b09558SBrian Somers                      dl->cbcp.fsm.phone);
69292b09558SBrian Somers           dl->cbcp.required = 1;
69392b09558SBrian Somers         }
69492b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
69592b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
69664cfdfc6SBrian Somers         datalink_AuthReInit(dl);
69792b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
69892b09558SBrian Somers         break;
69992b09558SBrian Somers 
70092b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
70192b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
70292b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
70392b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
70492b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
70592b09558SBrian Somers                    dl->cbcp.fsm.phone);
70692b09558SBrian Somers         dl->cbcp.required = 1;
70792b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
70892b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
70964cfdfc6SBrian Somers         datalink_AuthReInit(dl);
71092b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
71192b09558SBrian Somers         break;
71292b09558SBrian Somers 
71392b09558SBrian Somers       default:
71492b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
71592b09558SBrian Somers                    dl->name);
71692b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
71764cfdfc6SBrian Somers         datalink_AuthReInit(dl);
71892b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
71992b09558SBrian Somers         break;
72092b09558SBrian Somers     }
72192b09558SBrian Somers }
72292b09558SBrian Somers 
72392b09558SBrian Somers void
724e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
725e2ebb036SBrian Somers {
7269ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
72764cfdfc6SBrian Somers   datalink_AuthReInit(dl);
728dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
7296d666775SBrian Somers }
7306d666775SBrian Somers 
7316d666775SBrian Somers static void
7326d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
7336d666775SBrian Somers {
7346d666775SBrian Somers   /* The given FSM has been told to come down */
7356d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
736a611cad6SBrian Somers 
73792e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
738e2ebb036SBrian Somers     switch (dl->state) {
739e2ebb036SBrian Somers       case DATALINK_OPEN:
740643f4904SBrian Somers         peerid_Init(&dl->peer);
74109206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
742ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
743e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
74492b09558SBrian Somers         /* fall through (just in case) */
74592b09558SBrian Somers 
74692b09558SBrian Somers       case DATALINK_CBCP:
74792b09558SBrian Somers         if (!dl->cbcp.required)
74892b09558SBrian Somers           cbcp_Down(&dl->cbcp);
74992b09558SBrian Somers         /* fall through (just in case) */
750e2ebb036SBrian Somers 
751e2ebb036SBrian Somers       case DATALINK_AUTH:
752dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
753dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
7546d666775SBrian Somers     }
7559ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
75664cfdfc6SBrian Somers     datalink_AuthReInit(dl);
757e2ebb036SBrian Somers   }
7586d666775SBrian Somers }
7596d666775SBrian Somers 
7606d666775SBrian Somers static void
7616d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
7626d666775SBrian Somers {
7636d666775SBrian Somers   /* The given fsm is now down */
7646d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
7656d666775SBrian Somers 
76692e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
76709206a6fSBrian Somers     fsm2initial(fp);
7686d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
7699c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
7700a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
7710a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
7726d666775SBrian Somers }
7736d666775SBrian Somers 
7743006ec67SBrian Somers struct datalink *
7756f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
7763006ec67SBrian Somers {
7773006ec67SBrian Somers   struct datalink *dl;
7783006ec67SBrian Somers 
7793006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
7803006ec67SBrian Somers   if (dl == NULL)
7813006ec67SBrian Somers     return dl;
7823006ec67SBrian Somers 
7833006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
7843006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
7853006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
7863006ec67SBrian Somers   dl->desc.Read = datalink_Read;
7873006ec67SBrian Somers   dl->desc.Write = datalink_Write;
7885b8b8060SBrian Somers 
789e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
790e718d1d7SBrian Somers 
79173a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
79273a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
793c116e0c0SBrian Somers   *dl->cfg.script.logout = '\0';
79473a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
795f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
796f9545805SBrian Somers   *dl->phone.list = '\0';
797f9545805SBrian Somers   dl->phone.next = NULL;
798f9545805SBrian Somers   dl->phone.alt = NULL;
799f9545805SBrian Somers   dl->phone.chosen = "N/A";
8009c81b87dSBrian Somers   dl->stayonline = 0;
801c7cc5030SBrian Somers   dl->script.run = 1;
802c7cc5030SBrian Somers   dl->script.packetmode = 1;
8033b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
8045b8b8060SBrian Somers 
8053006ec67SBrian Somers   dl->bundle = bundle;
8063006ec67SBrian Somers   dl->next = NULL;
807e718d1d7SBrian Somers 
808c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
809e718d1d7SBrian Somers 
810c11e57a3SBrian Somers   dl->dial.tries = 0;
811565e35e5SBrian Somers   dl->cfg.dial.max = 1;
812565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
813565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
814c11e57a3SBrian Somers   dl->cfg.dial.inc = 0;
815c11e57a3SBrian Somers   dl->cfg.dial.maxinc = 10;
816e718d1d7SBrian Somers 
817abff9baeSBrian Somers   dl->reconnect_tries = 0;
818565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
819565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
820abff9baeSBrian Somers 
82192b09558SBrian Somers   dl->cfg.callback.opmask = 0;
82292b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
82392b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
82492b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
82592b09558SBrian Somers 
8263006ec67SBrian Somers   dl->name = strdup(name);
827643f4904SBrian Somers   peerid_Init(&dl->peer);
8286f384573SBrian Somers   dl->parent = &bundle->fsm;
8293b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
8303b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
8313b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
8323b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
8333b0f8d2eSBrian Somers   dl->fsmp.object = dl;
8343b0f8d2eSBrian Somers 
8355d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, type)) == NULL) {
8363006ec67SBrian Somers     free(dl->name);
8373006ec67SBrian Somers     free(dl);
8383006ec67SBrian Somers     return NULL;
8393006ec67SBrian Somers   }
840f0cdd9c0SBrian Somers 
841f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
842f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
84392b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
844c116e0c0SBrian Somers 
845c116e0c0SBrian Somers   memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
846ffcfaec7SBrian Somers   chat_Init(&dl->chat, dl->physical);
8473006ec67SBrian Somers 
8489ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
8499ae58882SBrian Somers              dl->name, datalink_State(dl));
8503006ec67SBrian Somers 
8513006ec67SBrian Somers   return dl;
8523006ec67SBrian Somers }
8533006ec67SBrian Somers 
8543006ec67SBrian Somers struct datalink *
855cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
856cd7bd93aSBrian Somers {
857cd7bd93aSBrian Somers   struct datalink *dl;
858cd7bd93aSBrian Somers 
859cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
860cd7bd93aSBrian Somers   if (dl == NULL)
861cd7bd93aSBrian Somers     return dl;
862cd7bd93aSBrian Somers 
863cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
864cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
865cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
866cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
867cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
868cd7bd93aSBrian Somers 
869cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
870cd7bd93aSBrian Somers 
871cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
872cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
873cd7bd93aSBrian Somers   *dl->phone.list = '\0';
874643f4904SBrian Somers   dl->phone.next = NULL;
875643f4904SBrian Somers   dl->phone.alt = NULL;
876643f4904SBrian Somers   dl->phone.chosen = "N/A";
877cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
878cd7bd93aSBrian Somers   dl->next = NULL;
879c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
880c11e57a3SBrian Somers   dl->dial.tries = 0;
881cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
882cd7bd93aSBrian Somers   dl->name = strdup(name);
883643f4904SBrian Somers   peerid_Init(&dl->peer);
884cd7bd93aSBrian Somers   dl->parent = odl->parent;
885cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
886643f4904SBrian Somers   dl->fsmp.object = dl;
887cd7bd93aSBrian Somers 
8885d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
889cd7bd93aSBrian Somers     free(dl->name);
890cd7bd93aSBrian Somers     free(dl);
891cd7bd93aSBrian Somers     return NULL;
892cd7bd93aSBrian Somers   }
893f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
894479508cfSBrian Somers   dl->pap.cfg = odl->pap.cfg;
895f0cdd9c0SBrian Somers 
896f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
897479508cfSBrian Somers   dl->chap.auth.cfg = odl->chap.auth.cfg;
898f0cdd9c0SBrian Somers 
899cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
900cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
901cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
902cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
903cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
904cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
905cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
906cd7bd93aSBrian Somers 
90792b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
908c116e0c0SBrian Somers 
909c116e0c0SBrian Somers   memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
910ffcfaec7SBrian Somers   chat_Init(&dl->chat, dl->physical);
911cd7bd93aSBrian Somers 
9129ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
9139ae58882SBrian Somers              dl->name, datalink_State(dl));
914cd7bd93aSBrian Somers 
915cd7bd93aSBrian Somers   return dl;
916cd7bd93aSBrian Somers }
917cd7bd93aSBrian Somers 
918cd7bd93aSBrian Somers struct datalink *
9193006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
9203006ec67SBrian Somers {
9213006ec67SBrian Somers   struct datalink *result;
9223006ec67SBrian Somers 
92339d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
924dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
925c7cc5030SBrian Somers               datalink_State(dl));
92639d94652SBrian Somers     switch (dl->state) {
92739d94652SBrian Somers       case DATALINK_HANGUP:
92839d94652SBrian Somers       case DATALINK_DIAL:
92939d94652SBrian Somers       case DATALINK_LOGIN:
930ffcfaec7SBrian Somers         chat_Finish(&dl->chat);		/* Gotta blat the timers ! */
93139d94652SBrian Somers         break;
93239d94652SBrian Somers     }
93339d94652SBrian Somers   }
9343006ec67SBrian Somers 
935ffcfaec7SBrian Somers   chat_Destroy(&dl->chat);
936c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
9373006ec67SBrian Somers   result = dl->next;
9385d9e6103SBrian Somers   physical_Destroy(dl->physical);
9393006ec67SBrian Somers   free(dl->name);
9403006ec67SBrian Somers   free(dl);
9413006ec67SBrian Somers 
9423006ec67SBrian Somers   return result;
9433006ec67SBrian Somers }
9443006ec67SBrian Somers 
9453006ec67SBrian Somers void
946c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
9473006ec67SBrian Somers {
9486f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
949565e35e5SBrian Somers     /* Ignore scripts */
950565e35e5SBrian Somers     runscripts = 0;
951565e35e5SBrian Somers 
952c7cc5030SBrian Somers   switch (dl->state) {
953c7cc5030SBrian Somers     case DATALINK_CLOSED:
9543b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
9553b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
9563b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
9579ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
958565e35e5SBrian Somers       dl->reconnect_tries =
9596f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
960c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
961c5a5a6caSBrian Somers       dl->script.run = runscripts;
962c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
963c7cc5030SBrian Somers       break;
964c7cc5030SBrian Somers 
965c7cc5030SBrian Somers     case DATALINK_OPENING:
966c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
967c7cc5030SBrian Somers         dl->script.run = 1;
968c7cc5030SBrian Somers       /* fall through */
969c7cc5030SBrian Somers 
970c7cc5030SBrian Somers     case DATALINK_DIAL:
971c7cc5030SBrian Somers     case DATALINK_LOGIN:
972c7cc5030SBrian Somers     case DATALINK_READY:
973c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
974c7cc5030SBrian Somers         dl->script.packetmode = 1;
9755e269efeSBrian Somers         if (dl->state == DATALINK_READY) {
9765e269efeSBrian Somers           dl->script.run = 0;
9775e269efeSBrian Somers           datalink_NewState(dl, DATALINK_CARRIER);
9785e269efeSBrian Somers         }
979c7cc5030SBrian Somers       }
980c7cc5030SBrian Somers       break;
9813006ec67SBrian Somers   }
9823006ec67SBrian Somers }
9833006ec67SBrian Somers 
9843006ec67SBrian Somers void
9859c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
986c7cc5030SBrian Somers {
987c7cc5030SBrian Somers   /* Please close */
988e2ebb036SBrian Somers   switch (dl->state) {
989e2ebb036SBrian Somers     case DATALINK_OPEN:
990643f4904SBrian Somers       peerid_Init(&dl->peer);
99109206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
992e2ebb036SBrian Somers       /* fall through */
993e2ebb036SBrian Somers 
99492b09558SBrian Somers     case DATALINK_CBCP:
995e2ebb036SBrian Somers     case DATALINK_AUTH:
996e2ebb036SBrian Somers     case DATALINK_LCP:
99764cfdfc6SBrian Somers       datalink_AuthReInit(dl);
9989c81b87dSBrian Somers       if (how == CLOSE_LCP)
9992fc2f705SBrian Somers         datalink_DontHangup(dl);
10002fc2f705SBrian Somers       else if (how == CLOSE_STAYDOWN)
10012fc2f705SBrian Somers         datalink_StayDown(dl);
10022fc2f705SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
1003d81ecf9aSBrian Somers       break;
1004e2ebb036SBrian Somers 
1005e2ebb036SBrian Somers     default:
10069c81b87dSBrian Somers       datalink_ComeDown(dl, how);
1007c7cc5030SBrian Somers   }
1008e2ebb036SBrian Somers }
1009c7cc5030SBrian Somers 
1010c7cc5030SBrian Somers void
10119c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
1012c7cc5030SBrian Somers {
1013c7cc5030SBrian Somers   /* Carrier is lost */
1014e2ebb036SBrian Somers   switch (dl->state) {
1015e2ebb036SBrian Somers     case DATALINK_OPEN:
1016643f4904SBrian Somers       peerid_Init(&dl->peer);
101709206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
1018e2ebb036SBrian Somers       /* fall through */
1019e2ebb036SBrian Somers 
102092b09558SBrian Somers     case DATALINK_CBCP:
1021e2ebb036SBrian Somers     case DATALINK_AUTH:
1022e2ebb036SBrian Somers     case DATALINK_LCP:
102309206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
1024225e259dSBrian Somers       if (dl->state == DATALINK_OPENING)
1025225e259dSBrian Somers         return;			/* we're doing a callback... */
1026e2ebb036SBrian Somers       /* fall through */
1027c7cc5030SBrian Somers 
1028e2ebb036SBrian Somers     default:
10299c81b87dSBrian Somers       datalink_ComeDown(dl, how);
1030c7cc5030SBrian Somers   }
1031e2ebb036SBrian Somers }
1032c7cc5030SBrian Somers 
1033c7cc5030SBrian Somers void
10343006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
10353006ec67SBrian Somers {
10362fc2f705SBrian Somers   dl->dial.tries = -1;
10373006ec67SBrian Somers   dl->reconnect_tries = 0;
10382fc2f705SBrian Somers   dl->stayonline = 0;
10393006ec67SBrian Somers }
1040c7cc5030SBrian Somers 
10419c81b87dSBrian Somers void
10429c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
10439c81b87dSBrian Somers {
10442fc2f705SBrian Somers   dl->dial.tries = -1;
10452fc2f705SBrian Somers   dl->reconnect_tries = 0;
10462fc2f705SBrian Somers   dl->stayonline = dl->state >= DATALINK_LCP ? 1 : 0;
10479c81b87dSBrian Somers }
10489c81b87dSBrian Somers 
1049643f4904SBrian Somers int
1050643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
1051c7cc5030SBrian Somers {
1052643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
1053643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
1054643f4904SBrian Somers                 datalink_State(arg->cx));
1055643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
1056643f4904SBrian Somers   if (*arg->cx->peer.authname)
1057643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
1058643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
1059643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
1060565e35e5SBrian Somers   else
1061643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
1062643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
1063643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
1064643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
1065643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
1066643f4904SBrian Somers 
1067643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
1068643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
1069643f4904SBrian Somers                 arg->cx->cfg.phone.list);
1070643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
1071643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
1072643f4904SBrian Somers                   arg->cx->cfg.dial.max);
1073565e35e5SBrian Somers   else
1074643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
1075b5c347a3SBrian Somers   if (arg->cx->cfg.dial.next_timeout >= 0)
1076643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
1077565e35e5SBrian Somers   else
1078643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
1079b5c347a3SBrian Somers   if (arg->cx->cfg.dial.timeout >= 0)
1080643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
1081565e35e5SBrian Somers   else
1082643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
1083643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
1084643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
1085643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
1086643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
1087643f4904SBrian Somers   else
1088643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
108992b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
109092b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
109192b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
109292b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
109392b09558SBrian Somers   else {
109492b09558SBrian Somers     int comma = 0;
109592b09558SBrian Somers 
109692b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
109792b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
109892b09558SBrian Somers       comma = 1;
109992b09558SBrian Somers     }
110092b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
110192b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
110292b09558SBrian Somers       comma = 1;
110392b09558SBrian Somers     }
110492b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
110592b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
110692b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
110792b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
110892b09558SBrian Somers       comma = 1;
110992b09558SBrian Somers     }
111092b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
111192b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
111292b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
111392b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
1114cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
1115cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
1116cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
1117cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
1118cf784a89SBrian Somers         else
1119cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
1120cf784a89SBrian Somers       } else
1121cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
112292b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
112392b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
112492b09558SBrian Somers     } else
112592b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
112692b09558SBrian Somers   }
112792b09558SBrian Somers 
1128643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1129643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1130643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1131643f4904SBrian Somers                 arg->cx->cfg.script.login);
1132c116e0c0SBrian Somers   prompt_Printf(arg->prompt, " Logout Script:      %s\n",
1133c116e0c0SBrian Somers                 arg->cx->cfg.script.logout);
1134643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1135643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1136643f4904SBrian Somers   return 0;
1137565e35e5SBrian Somers }
1138565e35e5SBrian Somers 
1139565e35e5SBrian Somers int
1140565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1141565e35e5SBrian Somers {
114225092092SBrian Somers   if (arg->argc == arg->argn+2) {
114325092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
114425092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1145565e35e5SBrian Somers     return 0;
1146565e35e5SBrian Somers   }
1147565e35e5SBrian Somers   return -1;
1148565e35e5SBrian Somers }
1149565e35e5SBrian Somers 
1150565e35e5SBrian Somers int
1151565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1152565e35e5SBrian Somers {
1153c11e57a3SBrian Somers   const char *sep, *osep;
1154c11e57a3SBrian Somers   int timeout, inc, maxinc, tries;
1155565e35e5SBrian Somers 
115625092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
115725092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
115825092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1159565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1160565e35e5SBrian Somers       randinit();
1161565e35e5SBrian Somers     } else {
116225092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1163565e35e5SBrian Somers 
1164565e35e5SBrian Somers       if (timeout >= 0)
1165565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1166565e35e5SBrian Somers       else {
1167dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1168565e35e5SBrian Somers 	return -1;
1169565e35e5SBrian Somers       }
1170565e35e5SBrian Somers     }
1171565e35e5SBrian Somers 
1172c11e57a3SBrian Somers     sep = strchr(arg->argv[arg->argn], '+');
1173c11e57a3SBrian Somers     if (sep) {
1174c11e57a3SBrian Somers       inc = atoi(++sep);
1175c11e57a3SBrian Somers       osep = sep;
1176c11e57a3SBrian Somers       if (inc >= 0)
1177c11e57a3SBrian Somers         arg->cx->cfg.dial.inc = inc;
1178c11e57a3SBrian Somers       else {
1179c11e57a3SBrian Somers         log_Printf(LogWARN, "Invalid timeout increment\n");
1180c11e57a3SBrian Somers         return -1;
1181c11e57a3SBrian Somers       }
1182c11e57a3SBrian Somers       sep = strchr(sep, '-');
1183c11e57a3SBrian Somers       if (sep) {
1184c11e57a3SBrian Somers         maxinc = atoi(++sep);
1185c11e57a3SBrian Somers         if (maxinc >= 0)
1186c11e57a3SBrian Somers           arg->cx->cfg.dial.maxinc = maxinc;
1187c11e57a3SBrian Somers         else {
1188c11e57a3SBrian Somers           log_Printf(LogWARN, "Invalid maximum timeout increments\n");
1189c11e57a3SBrian Somers           return -1;
1190c11e57a3SBrian Somers         }
1191c11e57a3SBrian Somers       } else {
1192c11e57a3SBrian Somers         /* Default timeout increment */
1193c11e57a3SBrian Somers         arg->cx->cfg.dial.maxinc = 10;
1194c11e57a3SBrian Somers         sep = osep;
1195c11e57a3SBrian Somers       }
1196c11e57a3SBrian Somers     } else {
1197c11e57a3SBrian Somers       /* Default timeout increment & max increment */
1198c11e57a3SBrian Somers       arg->cx->cfg.dial.inc = 0;
1199c11e57a3SBrian Somers       arg->cx->cfg.dial.maxinc = 10;
1200c11e57a3SBrian Somers       sep = arg->argv[arg->argn];
1201c11e57a3SBrian Somers     }
1202c11e57a3SBrian Somers 
1203c11e57a3SBrian Somers     sep = strchr(sep, '.');
1204c11e57a3SBrian Somers     if (sep) {
1205c11e57a3SBrian Somers       if (strcasecmp(++sep, "random") == 0) {
1206565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1207565e35e5SBrian Somers 	randinit();
1208565e35e5SBrian Somers       } else {
1209c11e57a3SBrian Somers 	timeout = atoi(sep);
1210565e35e5SBrian Somers 	if (timeout >= 0)
1211565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1212565e35e5SBrian Somers 	else {
1213dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1214565e35e5SBrian Somers 	  return -1;
1215565e35e5SBrian Somers 	}
1216565e35e5SBrian Somers       }
1217565e35e5SBrian Somers     } else
1218565e35e5SBrian Somers       /* Default next timeout */
1219565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1220565e35e5SBrian Somers 
122125092092SBrian Somers     if (arg->argc == arg->argn+2) {
122225092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1223565e35e5SBrian Somers 
1224565e35e5SBrian Somers       if (tries >= 0) {
1225565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1226565e35e5SBrian Somers       } else {
1227dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1228565e35e5SBrian Somers 	return 1;
1229565e35e5SBrian Somers       }
1230565e35e5SBrian Somers     }
1231565e35e5SBrian Somers     return 0;
1232565e35e5SBrian Somers   }
1233c11e57a3SBrian Somers 
1234565e35e5SBrian Somers   return -1;
1235c7cc5030SBrian Somers }
1236c7cc5030SBrian Somers 
1237182c898aSBrian Somers static const char * const states[] = {
1238565e35e5SBrian Somers   "closed",
1239565e35e5SBrian Somers   "opening",
1240565e35e5SBrian Somers   "hangup",
1241565e35e5SBrian Somers   "dial",
1242eb6e5e05SBrian Somers   "carrier",
1243c116e0c0SBrian Somers   "logout",
1244565e35e5SBrian Somers   "login",
1245565e35e5SBrian Somers   "ready",
1246565e35e5SBrian Somers   "lcp",
1247565e35e5SBrian Somers   "auth",
124892b09558SBrian Somers   "cbcp",
1249565e35e5SBrian Somers   "open"
1250c7cc5030SBrian Somers };
1251c7cc5030SBrian Somers 
1252643f4904SBrian Somers const char *
1253c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1254c7cc5030SBrian Somers {
1255c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1256c7cc5030SBrian Somers     return "unknown";
1257c7cc5030SBrian Somers   return states[dl->state];
1258c7cc5030SBrian Somers }
12596f384573SBrian Somers 
12609ae58882SBrian Somers static void
12619ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
12629ae58882SBrian Somers {
12639ae58882SBrian Somers   if (state != dl->state) {
12649ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
12659ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
12669ae58882SBrian Somers                  states[state]);
12679ae58882SBrian Somers       dl->state = state;
12689ae58882SBrian Somers     } else
12699ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
12709ae58882SBrian Somers   }
12719ae58882SBrian Somers }
12729ae58882SBrian Somers 
12736f384573SBrian Somers struct datalink *
127496c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
127587c3786eSBrian Somers              int fd, int *auxfd, int *nauxfd)
12766f384573SBrian Somers {
1277b7c5748eSBrian Somers   struct datalink *dl, *cdl;
1278479508cfSBrian Somers   struct fsm_retry copy;
1279b7c5748eSBrian Somers   char *oname;
12806f384573SBrian Somers 
128196c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
128296c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
12836f384573SBrian Somers 
128496c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
128596c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
128696c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
128796c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
12886f384573SBrian Somers   }
1289b7c5748eSBrian Somers 
1290b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1291b7c5748eSBrian Somers   oname = NULL;
1292b7c5748eSBrian Somers   do {
1293b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1294b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1295b7c5748eSBrian Somers         if (oname)
1296b7c5748eSBrian Somers           free(datalink_NextName(dl));
1297b7c5748eSBrian Somers         else
1298b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1299b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1300b7c5748eSBrian Somers       }
1301b7c5748eSBrian Somers   } while (cdl);
1302b7c5748eSBrian Somers 
1303b7c5748eSBrian Somers   if (oname) {
1304b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1305b7c5748eSBrian Somers     free(oname);
1306b7c5748eSBrian Somers   } else {
130796c9bb21SBrian Somers     dl->name = strdup(dl->name);
1308b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1309b7c5748eSBrian Somers   }
1310b7c5748eSBrian Somers   (*niov)++;
13116f384573SBrian Somers 
13126f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
13136f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
13146f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
13156f384573SBrian Somers   dl->desc.Read = datalink_Read;
13166f384573SBrian Somers   dl->desc.Write = datalink_Write;
13176f384573SBrian Somers 
13186f384573SBrian Somers   mp_linkInit(&dl->mp);
13196f384573SBrian Somers   *dl->phone.list = '\0';
13206f384573SBrian Somers   dl->phone.next = NULL;
13216f384573SBrian Somers   dl->phone.alt = NULL;
13226f384573SBrian Somers   dl->phone.chosen = "N/A";
13236f384573SBrian Somers 
13246f384573SBrian Somers   dl->bundle = bundle;
13256f384573SBrian Somers   dl->next = NULL;
1326c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
1327c11e57a3SBrian Somers   dl->dial.tries = 0;
13286f384573SBrian Somers   dl->reconnect_tries = 0;
13296f384573SBrian Somers   dl->parent = &bundle->fsm;
13306f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
13316f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
13326f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
13336f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
13346f384573SBrian Somers   dl->fsmp.object = dl;
13356f384573SBrian Somers 
133687c3786eSBrian Somers   dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
133796c9bb21SBrian Somers 
133896c9bb21SBrian Somers   if (!dl->physical) {
13396f384573SBrian Somers     free(dl->name);
13406f384573SBrian Somers     free(dl);
13416f384573SBrian Somers     dl = NULL;
13429ae58882SBrian Somers   } else {
1343479508cfSBrian Somers     copy = dl->pap.cfg.fsm;
1344f0cdd9c0SBrian Somers     pap_Init(&dl->pap, dl->physical);
1345479508cfSBrian Somers     dl->pap.cfg.fsm = copy;
1346f0cdd9c0SBrian Somers 
1347479508cfSBrian Somers     copy = dl->chap.auth.cfg.fsm;
1348f0cdd9c0SBrian Somers     chap_Init(&dl->chap, dl->physical);
1349479508cfSBrian Somers     dl->chap.auth.cfg.fsm = copy;
1350f0cdd9c0SBrian Somers 
135192b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
1352c116e0c0SBrian Somers 
1353c116e0c0SBrian Somers     memset(&dl->chat, '\0', sizeof dl->chat);	/* Force buf{start,end} reset */
1354ffcfaec7SBrian Somers     chat_Init(&dl->chat, dl->physical);
13556f384573SBrian Somers 
13569ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
13579ae58882SBrian Somers               dl->name, datalink_State(dl));
13589ae58882SBrian Somers   }
13599ae58882SBrian Somers 
13606f384573SBrian Somers   return dl;
13616f384573SBrian Somers }
13626f384573SBrian Somers 
13636f384573SBrian Somers int
136485fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
13652cb305afSBrian Somers              int *auxfd, int *nauxfd)
13666f384573SBrian Somers {
136796c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
136896c9bb21SBrian Somers   int link_fd;
13696f384573SBrian Somers 
137096c9bb21SBrian Somers   if (dl) {
1371c11e57a3SBrian Somers     timer_Stop(&dl->dial.timer);
137292b09558SBrian Somers     /* The following is purely for the sake of paranoia */
137392b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1374dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1375dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
13766f384573SBrian Somers   }
13776f384573SBrian Somers 
137896c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
137996c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
138096c9bb21SBrian Somers     if (dl) {
13816f384573SBrian Somers       free(dl->name);
13826f384573SBrian Somers       free(dl);
138396c9bb21SBrian Somers     }
138496c9bb21SBrian Somers     return -1;
138596c9bb21SBrian Somers   }
138696c9bb21SBrian Somers 
13872cb305afSBrian Somers   iov[*niov].iov_base = (void *)dl;
138896c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
13892cb305afSBrian Somers   iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
139096c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
139196c9bb21SBrian Somers 
139287c3786eSBrian Somers   link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
13932cb305afSBrian Somers                          nauxfd);
139496c9bb21SBrian Somers 
139596c9bb21SBrian Somers   if (link_fd == -1 && dl) {
139696c9bb21SBrian Somers     free(dl->name);
139796c9bb21SBrian Somers     free(dl);
139896c9bb21SBrian Somers   }
13996f384573SBrian Somers 
14006f384573SBrian Somers   return link_fd;
14016f384573SBrian Somers }
14026f384573SBrian Somers 
140358d55334SBrian Somers void
140458d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
140558d55334SBrian Somers {
140658d55334SBrian Somers   free(dl->name);
140758d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
140858d55334SBrian Somers }
140958d55334SBrian Somers 
141084917b87SBrian Somers char *
141184917b87SBrian Somers datalink_NextName(struct datalink *dl)
14126f384573SBrian Somers {
14136f384573SBrian Somers   int f, n;
141484917b87SBrian Somers   char *name, *oname;
14156f384573SBrian Somers 
14166f384573SBrian Somers   n = strlen(dl->name);
14176f384573SBrian Somers   name = (char *)malloc(n+3);
14186f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
14196f384573SBrian Somers     if (!isdigit(dl->name[f]))
14206f384573SBrian Somers       break;
14216f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
14226f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
142384917b87SBrian Somers   oname = dl->name;
142454cd8e13SBrian Somers   dl->name = name;
142554cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
142684917b87SBrian Somers   return oname;
14276f384573SBrian Somers }
1428dd0645c5SBrian Somers 
1429dd0645c5SBrian Somers int
1430dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1431dd0645c5SBrian Somers {
1432dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1433dd0645c5SBrian Somers     return 0;
1434dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1435dd0645c5SBrian Somers     dl->script.run = 0;
1436dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1437dd0645c5SBrian Somers     dl->reconnect_tries = 0;
1438f6a4e748SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
1439f6a4e748SBrian Somers       dl->state <= DATALINK_READY)
1440dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1441dd0645c5SBrian Somers   return 1;
1442dd0645c5SBrian Somers }
1443c11e57a3SBrian Somers 
1444c11e57a3SBrian Somers int
1445c11e57a3SBrian Somers datalink_GetDialTimeout(struct datalink *dl)
1446c11e57a3SBrian Somers {
1447c11e57a3SBrian Somers   int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
1448c11e57a3SBrian Somers 
1449c11e57a3SBrian Somers   if (dl->dial.incs < dl->cfg.dial.maxinc)
1450c11e57a3SBrian Somers     dl->dial.incs++;
1451c11e57a3SBrian Somers 
1452c11e57a3SBrian Somers   return result;
1453c11e57a3SBrian Somers }
1454