xref: /freebsd/usr.sbin/ppp/datalink.c (revision 06337856)
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  *
2606337856SBrian Somers  *	$Id: datalink.c,v 1.14 1998/06/27 14:18:04 brian Exp $
273006ec67SBrian Somers  */
283006ec67SBrian Somers 
292764b86aSBrian Somers #include <sys/types.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 
423006ec67SBrian Somers #include "mbuf.h"
433006ec67SBrian Somers #include "log.h"
443006ec67SBrian Somers #include "defs.h"
453006ec67SBrian Somers #include "timer.h"
463006ec67SBrian Somers #include "fsm.h"
473006ec67SBrian Somers #include "lcp.h"
483006ec67SBrian Somers #include "descriptor.h"
49879ed6faSBrian Somers #include "lqr.h"
503006ec67SBrian Somers #include "hdlc.h"
513006ec67SBrian Somers #include "async.h"
523006ec67SBrian Somers #include "throughput.h"
533b0f8d2eSBrian Somers #include "ccp.h"
543006ec67SBrian Somers #include "link.h"
553006ec67SBrian Somers #include "physical.h"
565828db6dSBrian Somers #include "iplist.h"
57eaa4df37SBrian Somers #include "slcompress.h"
585828db6dSBrian Somers #include "ipcp.h"
595ca5389aSBrian Somers #include "filter.h"
603b0f8d2eSBrian Somers #include "mp.h"
613006ec67SBrian Somers #include "bundle.h"
623006ec67SBrian Somers #include "chat.h"
63e2ebb036SBrian Somers #include "auth.h"
643006ec67SBrian Somers #include "modem.h"
65c7cc5030SBrian Somers #include "prompt.h"
66e2ebb036SBrian Somers #include "lcpproto.h"
67e2ebb036SBrian Somers #include "pap.h"
68e2ebb036SBrian Somers #include "chap.h"
69565e35e5SBrian Somers #include "command.h"
70e2ebb036SBrian Somers #include "datalink.h"
71c7cc5030SBrian Somers 
72030e4ebbSBrian Somers static void datalink_LoginDone(struct datalink *);
739ae58882SBrian Somers static void datalink_NewState(struct datalink *, int);
743006ec67SBrian Somers 
753006ec67SBrian Somers static void
763006ec67SBrian Somers datalink_OpenTimeout(void *v)
773006ec67SBrian Somers {
783006ec67SBrian Somers   struct datalink *dl = (struct datalink *)v;
793006ec67SBrian Somers 
80dd7e2610SBrian Somers   timer_Stop(&dl->dial_timer);
813006ec67SBrian Somers   if (dl->state == DATALINK_OPENING)
82dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
833006ec67SBrian Somers }
843006ec67SBrian Somers 
853006ec67SBrian Somers static void
863006ec67SBrian Somers datalink_StartDialTimer(struct datalink *dl, int Timeout)
873006ec67SBrian Somers {
88dd7e2610SBrian Somers   timer_Stop(&dl->dial_timer);
893006ec67SBrian Somers 
903006ec67SBrian Somers   if (Timeout) {
913006ec67SBrian Somers     if (Timeout > 0)
923006ec67SBrian Somers       dl->dial_timer.load = Timeout * SECTICKS;
933006ec67SBrian Somers     else
94e718d1d7SBrian Somers       dl->dial_timer.load = (random() % DIAL_TIMEOUT) * SECTICKS;
953006ec67SBrian Somers     dl->dial_timer.func = datalink_OpenTimeout;
963b0f8d2eSBrian Somers     dl->dial_timer.name = "dial";
973006ec67SBrian Somers     dl->dial_timer.arg = dl;
98dd7e2610SBrian Somers     timer_Start(&dl->dial_timer);
993006ec67SBrian Somers     if (dl->state == DATALINK_OPENING)
100dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
1013006ec67SBrian Somers                 dl->name, Timeout);
1023006ec67SBrian Somers   }
1033006ec67SBrian Somers }
1043006ec67SBrian Somers 
1053006ec67SBrian Somers static void
1063006ec67SBrian Somers datalink_HangupDone(struct datalink *dl)
1073006ec67SBrian Somers {
108030e4ebbSBrian Somers   if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
109dd7e2610SBrian Somers       physical_GetFD(dl->physical) != -1) {
110030e4ebbSBrian Somers     /* Don't close our modem if the link is dedicated */
111030e4ebbSBrian Somers     datalink_LoginDone(dl);
112030e4ebbSBrian Somers     return;
113030e4ebbSBrian Somers   }
114030e4ebbSBrian Somers 
1153006ec67SBrian Somers   modem_Close(dl->physical);
116565e35e5SBrian Somers   dl->phone.chosen = "N/A";
1173006ec67SBrian Somers 
1183b0f8d2eSBrian Somers   if (dl->bundle->CleaningUp ||
1196f384573SBrian Somers       (dl->physical->type == PHYS_DIRECT) ||
1203b0f8d2eSBrian Somers       ((!dl->dial_tries || (dl->dial_tries < 0 && !dl->reconnect_tries)) &&
12181358fa3SBrian Somers        !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
1229ae58882SBrian Somers     datalink_NewState(dl, DATALINK_CLOSED);
1233006ec67SBrian Somers     dl->dial_tries = -1;
1243006ec67SBrian Somers     dl->reconnect_tries = 0;
1253006ec67SBrian Somers     bundle_LinkClosed(dl->bundle, dl);
1263b0f8d2eSBrian Somers     if (!dl->bundle->CleaningUp)
127565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
1283006ec67SBrian Somers   } else {
1299ae58882SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
130abff9baeSBrian Somers     if (dl->dial_tries < 0) {
131565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
132565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
133abff9baeSBrian Somers       dl->reconnect_tries--;
134abff9baeSBrian Somers     } else {
135f9545805SBrian Somers       if (dl->phone.next == NULL)
136565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
1373006ec67SBrian Somers       else
138565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
1393006ec67SBrian Somers     }
140abff9baeSBrian Somers   }
141abff9baeSBrian Somers }
1423006ec67SBrian Somers 
143f9545805SBrian Somers static const char *
144f9545805SBrian Somers datalink_ChoosePhoneNumber(struct datalink *dl)
145f9545805SBrian Somers {
146f9545805SBrian Somers   char *phone;
147f9545805SBrian Somers 
148f9545805SBrian Somers   if (dl->phone.alt == NULL) {
149f9545805SBrian Somers     if (dl->phone.next == NULL) {
150f9545805SBrian Somers       strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
151f9545805SBrian Somers       dl->phone.list[sizeof dl->phone.list - 1] = '\0';
152f9545805SBrian Somers       dl->phone.next = dl->phone.list;
153f9545805SBrian Somers     }
154f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
155f9545805SBrian Somers   }
156f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
157f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
158f9545805SBrian Somers   if (*phone)
159dd7e2610SBrian Somers     log_Printf(LogPHASE, "Phone: %s\n", phone);
160f9545805SBrian Somers   return phone;
161f9545805SBrian Somers }
162f9545805SBrian Somers 
163c5a5a6caSBrian Somers static void
164c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
165c5a5a6caSBrian Somers {
166d345321bSBrian Somers   if (!dl->script.packetmode) {
167d345321bSBrian Somers     dl->dial_tries = -1;
1689ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
169d345321bSBrian Somers   } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
170c5a5a6caSBrian Somers     dl->dial_tries = 0;
171dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
172c5a5a6caSBrian Somers     if (dl->script.run) {
1739ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
174c5a5a6caSBrian Somers       modem_Offline(dl->physical);
175f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
176030e4ebbSBrian Somers     } else {
177030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
178030e4ebbSBrian Somers         /* force a redial timeout */
179030e4ebbSBrian Somers         modem_Close(dl->physical);
180c5a5a6caSBrian Somers       datalink_HangupDone(dl);
181030e4ebbSBrian Somers     }
182c5a5a6caSBrian Somers   } else {
183c7cc5030SBrian Somers     dl->dial_tries = -1;
184c7cc5030SBrian Somers 
185643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
1863b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
1873b0f8d2eSBrian Somers 
188cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
189cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
1903b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
191503a7782SBrian Somers 
1929ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
193dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
194dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
195c5a5a6caSBrian Somers   }
196c5a5a6caSBrian Somers }
197c5a5a6caSBrian Somers 
1983006ec67SBrian Somers static int
1993006ec67SBrian Somers datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
2003006ec67SBrian Somers                    int *n)
2013006ec67SBrian Somers {
2023006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2033006ec67SBrian Somers   int result;
2043006ec67SBrian Somers 
205310c3babSBrian Somers   result = 0;
2063006ec67SBrian Somers   switch (dl->state) {
2073006ec67SBrian Somers     case DATALINK_CLOSED:
20881358fa3SBrian Somers       if ((dl->physical->type &
20981358fa3SBrian Somers            (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
210565e35e5SBrian Somers           !bundle_IsDead(dl->bundle))
211565e35e5SBrian Somers         /*
21281358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
21381358fa3SBrian Somers          * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
21481358fa3SBrian Somers          * Go to DATALINK_OPENING via datalink_Up() and fall through.
215565e35e5SBrian Somers          */
216565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
217565e35e5SBrian Somers       else
218310c3babSBrian Somers         break;
219565e35e5SBrian Somers       /* fall through */
2203006ec67SBrian Somers 
2213006ec67SBrian Somers     case DATALINK_OPENING:
2223006ec67SBrian Somers       if (dl->dial_timer.state != TIMER_RUNNING) {
2233006ec67SBrian Somers         if (--dl->dial_tries < 0)
2243006ec67SBrian Somers           dl->dial_tries = 0;
2253006ec67SBrian Somers         if (modem_Open(dl->physical, dl->bundle) >= 0) {
226c5a5a6caSBrian Somers           if (dl->script.run) {
2279ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
228f9545805SBrian Somers             chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
229f9545805SBrian Somers                       datalink_ChoosePhoneNumber(dl));
23081358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
231565e35e5SBrian Somers                 dl->cfg.dial.max)
232dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
233565e35e5SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial_tries,
234565e35e5SBrian Somers                         dl->cfg.dial.max);
2351342caedSBrian Somers             return datalink_UpdateSet(d, r, w, e, n);
236c5a5a6caSBrian Somers           } else
237c5a5a6caSBrian Somers             datalink_LoginDone(dl);
2383006ec67SBrian Somers         } else {
23981358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
240565e35e5SBrian Somers               dl->cfg.dial.max)
241dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
242565e35e5SBrian Somers                       dl->cfg.dial.max - dl->dial_tries, dl->cfg.dial.max);
2433006ec67SBrian Somers           else
244dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem\n");
2453006ec67SBrian Somers 
2463b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
24781358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
248565e35e5SBrian Somers                dl->cfg.dial.max && dl->dial_tries == 0)) {
2499ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
2503006ec67SBrian Somers             dl->reconnect_tries = 0;
2513006ec67SBrian Somers             dl->dial_tries = -1;
2525b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
253c5a5a6caSBrian Somers           }
2543b0f8d2eSBrian Somers           if (!dl->bundle->CleaningUp)
255565e35e5SBrian Somers             datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
2563006ec67SBrian Somers         }
2573006ec67SBrian Somers       }
258310c3babSBrian Somers       break;
2593006ec67SBrian Somers 
2603006ec67SBrian Somers     case DATALINK_HANGUP:
2613006ec67SBrian Somers     case DATALINK_DIAL:
2623006ec67SBrian Somers     case DATALINK_LOGIN:
2633006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
2643006ec67SBrian Somers       switch (dl->chat.state) {
2653006ec67SBrian Somers         case CHAT_DONE:
2663006ec67SBrian Somers           /* script succeeded */
26739d94652SBrian Somers           chat_Destroy(&dl->chat);
2683006ec67SBrian Somers           switch(dl->state) {
2693006ec67SBrian Somers             case DATALINK_HANGUP:
2703006ec67SBrian Somers               datalink_HangupDone(dl);
2713006ec67SBrian Somers               break;
2723006ec67SBrian Somers             case DATALINK_DIAL:
2739ae58882SBrian Somers               datalink_NewState(dl, DATALINK_LOGIN);
274f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
2751342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
2763006ec67SBrian Somers             case DATALINK_LOGIN:
277c5a5a6caSBrian Somers               datalink_LoginDone(dl);
2783006ec67SBrian Somers               break;
2793006ec67SBrian Somers           }
2803006ec67SBrian Somers           break;
2813006ec67SBrian Somers         case CHAT_FAILED:
2823006ec67SBrian Somers           /* Going down - script failed */
283dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
28439d94652SBrian Somers           chat_Destroy(&dl->chat);
2853006ec67SBrian Somers           switch(dl->state) {
2863006ec67SBrian Somers             case DATALINK_HANGUP:
2873006ec67SBrian Somers               datalink_HangupDone(dl);
2883006ec67SBrian Somers               break;
2893006ec67SBrian Somers             case DATALINK_DIAL:
2903006ec67SBrian Somers             case DATALINK_LOGIN:
2919ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
2923006ec67SBrian Somers               modem_Offline(dl->physical);
293f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
2941342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
2953006ec67SBrian Somers           }
2963006ec67SBrian Somers           break;
2973006ec67SBrian Somers       }
2983006ec67SBrian Somers       break;
299c7cc5030SBrian Somers 
300c7cc5030SBrian Somers     case DATALINK_READY:
301e2ebb036SBrian Somers     case DATALINK_LCP:
302e2ebb036SBrian Somers     case DATALINK_AUTH:
3033006ec67SBrian Somers     case DATALINK_OPEN:
3043006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3053006ec67SBrian Somers       break;
3063006ec67SBrian Somers   }
3073006ec67SBrian Somers   return result;
3083006ec67SBrian Somers }
3093006ec67SBrian Somers 
310ea722969SBrian Somers int
311ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
312ea722969SBrian Somers {
313ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
314ea722969SBrian Somers }
315ea722969SBrian Somers 
3163006ec67SBrian Somers static int
3172f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3183006ec67SBrian Somers {
3193006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3203006ec67SBrian Somers 
3213006ec67SBrian Somers   switch (dl->state) {
3223006ec67SBrian Somers     case DATALINK_CLOSED:
323c7cc5030SBrian Somers     case DATALINK_OPENING:
3243006ec67SBrian Somers       break;
325c7cc5030SBrian Somers 
3263006ec67SBrian Somers     case DATALINK_HANGUP:
3273006ec67SBrian Somers     case DATALINK_DIAL:
3283006ec67SBrian Somers     case DATALINK_LOGIN:
3293006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
330c7cc5030SBrian Somers 
331c7cc5030SBrian Somers     case DATALINK_READY:
332e2ebb036SBrian Somers     case DATALINK_LCP:
333e2ebb036SBrian Somers     case DATALINK_AUTH:
3343006ec67SBrian Somers     case DATALINK_OPEN:
3353006ec67SBrian Somers       return descriptor_IsSet(&dl->physical->desc, fdset);
3363006ec67SBrian Somers   }
3373006ec67SBrian Somers   return 0;
3383006ec67SBrian Somers }
3393006ec67SBrian Somers 
3403006ec67SBrian Somers static void
3413006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3423006ec67SBrian Somers {
3433006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3443006ec67SBrian Somers 
3453006ec67SBrian Somers   switch (dl->state) {
3463006ec67SBrian Somers     case DATALINK_CLOSED:
347c7cc5030SBrian Somers     case DATALINK_OPENING:
3483006ec67SBrian Somers       break;
349c7cc5030SBrian Somers 
3503006ec67SBrian Somers     case DATALINK_HANGUP:
3513006ec67SBrian Somers     case DATALINK_DIAL:
3523006ec67SBrian Somers     case DATALINK_LOGIN:
3533006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
3543006ec67SBrian Somers       break;
355c7cc5030SBrian Somers 
356c7cc5030SBrian Somers     case DATALINK_READY:
357e2ebb036SBrian Somers     case DATALINK_LCP:
358e2ebb036SBrian Somers     case DATALINK_AUTH:
3593006ec67SBrian Somers     case DATALINK_OPEN:
3603006ec67SBrian Somers       descriptor_Read(&dl->physical->desc, bundle, fdset);
3613006ec67SBrian Somers       break;
3623006ec67SBrian Somers   }
3633006ec67SBrian Somers }
3643006ec67SBrian Somers 
3651af29a6eSBrian Somers static int
366f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3673006ec67SBrian Somers {
3683006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3691af29a6eSBrian Somers   int result = 0;
3703006ec67SBrian Somers 
3713006ec67SBrian Somers   switch (dl->state) {
3723006ec67SBrian Somers     case DATALINK_CLOSED:
373c7cc5030SBrian Somers     case DATALINK_OPENING:
3743006ec67SBrian Somers       break;
375c7cc5030SBrian Somers 
3763006ec67SBrian Somers     case DATALINK_HANGUP:
3773006ec67SBrian Somers     case DATALINK_DIAL:
3783006ec67SBrian Somers     case DATALINK_LOGIN:
3791af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
3803006ec67SBrian Somers       break;
381c7cc5030SBrian Somers 
382c7cc5030SBrian Somers     case DATALINK_READY:
383e2ebb036SBrian Somers     case DATALINK_LCP:
384e2ebb036SBrian Somers     case DATALINK_AUTH:
3853006ec67SBrian Somers     case DATALINK_OPEN:
3861af29a6eSBrian Somers       result = descriptor_Write(&dl->physical->desc, bundle, fdset);
3873006ec67SBrian Somers       break;
3883006ec67SBrian Somers   }
3891af29a6eSBrian Somers 
3901af29a6eSBrian Somers   return result;
3913006ec67SBrian Somers }
3923006ec67SBrian Somers 
3936d666775SBrian Somers static void
3949c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
395e2ebb036SBrian Somers {
3969c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
397e2ebb036SBrian Somers     dl->dial_tries = -1;
398e2ebb036SBrian Somers     dl->reconnect_tries = 0;
3997729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4009c81b87dSBrian Somers       dl->stayonline = 1;
401e2ebb036SBrian Somers   }
402e2ebb036SBrian Somers 
4037729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4049c81b87dSBrian Somers     dl->stayonline = 0;
4059c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4069c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
407e2ebb036SBrian Somers     modem_Offline(dl->physical);
408e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4099ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
410f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
411e2ebb036SBrian Somers     } else
412e2ebb036SBrian Somers       datalink_HangupDone(dl);
413e2ebb036SBrian Somers   }
414e2ebb036SBrian Somers }
415e2ebb036SBrian Somers 
416e2ebb036SBrian Somers static void
4176d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4186d666775SBrian Somers {
4196d666775SBrian Somers   /* The given FSM is about to start up ! */
4206d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
421a611cad6SBrian Somers 
42292e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
423e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
424e2ebb036SBrian Somers }
4256d666775SBrian Somers 
4266d666775SBrian Somers static void
4276d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4286d666775SBrian Somers {
4296d666775SBrian Somers   /* The given fsm is now up */
4306d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4316d666775SBrian Somers 
43292e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
433643f4904SBrian Somers     datalink_GotAuthname(dl, "", 0);
4343b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth;
4353b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth;
4363b0f8d2eSBrian Somers     if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) {
4373b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH)
4385563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
439dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
4403b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.his_auth),
4413b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.want_auth));
4423b0f8d2eSBrian Somers       if (dl->physical->link.lcp.his_auth == PROTO_PAP)
443dd7e2610SBrian Somers         auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge);
4443b0f8d2eSBrian Somers       if (dl->physical->link.lcp.want_auth == PROTO_CHAP)
445dd7e2610SBrian Somers         auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge);
446e2ebb036SBrian Somers     } else
447e2ebb036SBrian Somers       datalink_AuthOk(dl);
448e2ebb036SBrian Somers   }
449e2ebb036SBrian Somers }
450e2ebb036SBrian Somers 
451e2ebb036SBrian Somers void
452643f4904SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name, int len)
453643f4904SBrian Somers {
454643f4904SBrian Somers   if (len >= sizeof dl->peer.authname)
455643f4904SBrian Somers     len = sizeof dl->peer.authname - 1;
456643f4904SBrian Somers   strncpy(dl->peer.authname, name, len);
457643f4904SBrian Somers   dl->peer.authname[len] = '\0';
458643f4904SBrian Somers }
459643f4904SBrian Somers 
460643f4904SBrian Somers void
461e2ebb036SBrian Somers datalink_AuthOk(struct datalink *dl)
462e2ebb036SBrian Somers {
46306337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
4641df0a3b9SBrian Somers 
465dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
4661fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
4671fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
4681fa665f5SBrian Somers       case MP_LINKSENT:
469ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
4701fa665f5SBrian Somers         return;
4711fa665f5SBrian Somers       case MP_UP:
4720a1b5c9dSBrian Somers         /* First link in the bundle */
473dd7e2610SBrian Somers         auth_Select(dl->bundle, dl->peer.authname, dl->physical);
4740a1b5c9dSBrian Somers         /* fall through */
4751fa665f5SBrian Somers       case MP_ADDED:
4760a1b5c9dSBrian Somers         /* We're in multilink mode ! */
4771df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
4781fa665f5SBrian Somers         break;
4791fa665f5SBrian Somers       case MP_FAILED:
48049052c95SBrian Somers         datalink_AuthNotOk(dl);
48149052c95SBrian Somers         return;
48249052c95SBrian Somers     }
48349052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
484dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
485897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
486897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
48749052c95SBrian Somers     return;
48850abd4c8SBrian Somers   } else {
48950abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
490ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
491dd7e2610SBrian Somers     auth_Select(dl->bundle, dl->peer.authname, dl->physical);
49250abd4c8SBrian Somers   }
49349052c95SBrian Somers 
49406337856SBrian Somers   if (ccpok) {
495dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
496dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
49706337856SBrian Somers   }
4989ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
4993b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5003b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5016d666775SBrian Somers }
502e2ebb036SBrian Somers 
503e2ebb036SBrian Somers void
504e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
505e2ebb036SBrian Somers {
5069ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
507dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
5086d666775SBrian Somers }
5096d666775SBrian Somers 
5106d666775SBrian Somers static void
5116d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
5126d666775SBrian Somers {
5136d666775SBrian Somers   /* The given FSM has been told to come down */
5146d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
515a611cad6SBrian Somers 
51692e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
517e2ebb036SBrian Somers     switch (dl->state) {
518e2ebb036SBrian Somers       case DATALINK_OPEN:
519643f4904SBrian Somers         peerid_Init(&dl->peer);
52009206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
521ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
522e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
523e2ebb036SBrian Somers         /* fall through */
524e2ebb036SBrian Somers 
525e2ebb036SBrian Somers       case DATALINK_AUTH:
526dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
527dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
5286d666775SBrian Somers     }
5299ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
530e2ebb036SBrian Somers   }
5316d666775SBrian Somers }
5326d666775SBrian Somers 
5336d666775SBrian Somers static void
5346d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
5356d666775SBrian Somers {
5366d666775SBrian Somers   /* The given fsm is now down */
5376d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
5386d666775SBrian Somers 
53992e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
54009206a6fSBrian Somers     fsm2initial(fp);
5416d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
5429c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
5430a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
5440a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
5456d666775SBrian Somers }
5466d666775SBrian Somers 
5473006ec67SBrian Somers struct datalink *
5486f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
5493006ec67SBrian Somers {
5503006ec67SBrian Somers   struct datalink *dl;
5513006ec67SBrian Somers 
5523006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
5533006ec67SBrian Somers   if (dl == NULL)
5543006ec67SBrian Somers     return dl;
5553006ec67SBrian Somers 
5563006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
5573006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
5583006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
5593006ec67SBrian Somers   dl->desc.Read = datalink_Read;
5603006ec67SBrian Somers   dl->desc.Write = datalink_Write;
5615b8b8060SBrian Somers 
562e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
563e718d1d7SBrian Somers 
56473a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
56573a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
56673a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
567f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
568f9545805SBrian Somers   *dl->phone.list = '\0';
569f9545805SBrian Somers   dl->phone.next = NULL;
570f9545805SBrian Somers   dl->phone.alt = NULL;
571f9545805SBrian Somers   dl->phone.chosen = "N/A";
5729c81b87dSBrian Somers   dl->stayonline = 0;
573c7cc5030SBrian Somers   dl->script.run = 1;
574c7cc5030SBrian Somers   dl->script.packetmode = 1;
5753b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
5765b8b8060SBrian Somers 
5773006ec67SBrian Somers   dl->bundle = bundle;
5783006ec67SBrian Somers   dl->next = NULL;
579e718d1d7SBrian Somers 
5803006ec67SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
581e718d1d7SBrian Somers 
5823006ec67SBrian Somers   dl->dial_tries = 0;
583565e35e5SBrian Somers   dl->cfg.dial.max = 1;
584565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
585565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
586e718d1d7SBrian Somers 
587abff9baeSBrian Somers   dl->reconnect_tries = 0;
588565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
589565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
590abff9baeSBrian Somers 
5913006ec67SBrian Somers   dl->name = strdup(name);
592643f4904SBrian Somers   peerid_Init(&dl->peer);
5936f384573SBrian Somers   dl->parent = &bundle->fsm;
5943b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
5953b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
5963b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
5973b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
5983b0f8d2eSBrian Somers   dl->fsmp.object = dl;
5993b0f8d2eSBrian Somers 
600dd7e2610SBrian Somers   auth_Init(&dl->pap);
601dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
6023b0f8d2eSBrian Somers 
603565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
6043006ec67SBrian Somers     free(dl->name);
6053006ec67SBrian Somers     free(dl);
6063006ec67SBrian Somers     return NULL;
6073006ec67SBrian Somers   }
608f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
6093006ec67SBrian Somers 
6109ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
6119ae58882SBrian Somers              dl->name, datalink_State(dl));
6123006ec67SBrian Somers 
6133006ec67SBrian Somers   return dl;
6143006ec67SBrian Somers }
6153006ec67SBrian Somers 
6163006ec67SBrian Somers struct datalink *
617cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
618cd7bd93aSBrian Somers {
619cd7bd93aSBrian Somers   struct datalink *dl;
620cd7bd93aSBrian Somers 
621cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
622cd7bd93aSBrian Somers   if (dl == NULL)
623cd7bd93aSBrian Somers     return dl;
624cd7bd93aSBrian Somers 
625cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
626cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
627cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
628cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
629cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
630cd7bd93aSBrian Somers 
631cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
632cd7bd93aSBrian Somers 
633cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
634cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
635cd7bd93aSBrian Somers   *dl->phone.list = '\0';
636643f4904SBrian Somers   dl->phone.next = NULL;
637643f4904SBrian Somers   dl->phone.alt = NULL;
638643f4904SBrian Somers   dl->phone.chosen = "N/A";
639cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
640cd7bd93aSBrian Somers   dl->next = NULL;
641cd7bd93aSBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
642cd7bd93aSBrian Somers   dl->dial_tries = 0;
643cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
644cd7bd93aSBrian Somers   dl->name = strdup(name);
645643f4904SBrian Somers   peerid_Init(&dl->peer);
646cd7bd93aSBrian Somers   dl->parent = odl->parent;
647cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
648643f4904SBrian Somers   dl->fsmp.object = dl;
649dd7e2610SBrian Somers   auth_Init(&dl->pap);
650cd7bd93aSBrian Somers   dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry;
651cd7bd93aSBrian Somers 
652dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
653cd7bd93aSBrian Somers   dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry;
654cd7bd93aSBrian Somers 
65581358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
656cd7bd93aSBrian Somers     free(dl->name);
657cd7bd93aSBrian Somers     free(dl);
658cd7bd93aSBrian Somers     return NULL;
659cd7bd93aSBrian Somers   }
660cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
661cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
662cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
663cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
664cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
665cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
666cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
667cd7bd93aSBrian Somers 
668cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
669cd7bd93aSBrian Somers 
6709ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
6719ae58882SBrian Somers              dl->name, datalink_State(dl));
672cd7bd93aSBrian Somers 
673cd7bd93aSBrian Somers   return dl;
674cd7bd93aSBrian Somers }
675cd7bd93aSBrian Somers 
676cd7bd93aSBrian Somers struct datalink *
6773006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
6783006ec67SBrian Somers {
6793006ec67SBrian Somers   struct datalink *result;
6803006ec67SBrian Somers 
68139d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
682dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
683c7cc5030SBrian Somers               datalink_State(dl));
68439d94652SBrian Somers     switch (dl->state) {
68539d94652SBrian Somers       case DATALINK_HANGUP:
68639d94652SBrian Somers       case DATALINK_DIAL:
68739d94652SBrian Somers       case DATALINK_LOGIN:
68839d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
68939d94652SBrian Somers         break;
69039d94652SBrian Somers     }
69139d94652SBrian Somers   }
6923006ec67SBrian Somers 
6933006ec67SBrian Somers   result = dl->next;
6943b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
6953006ec67SBrian Somers   free(dl->name);
6963006ec67SBrian Somers   free(dl);
6973006ec67SBrian Somers 
6983006ec67SBrian Somers   return result;
6993006ec67SBrian Somers }
7003006ec67SBrian Somers 
7013006ec67SBrian Somers void
702c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
7033006ec67SBrian Somers {
7046f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
705565e35e5SBrian Somers     /* Ignore scripts */
706565e35e5SBrian Somers     runscripts = 0;
707565e35e5SBrian Somers 
708c7cc5030SBrian Somers   switch (dl->state) {
709c7cc5030SBrian Somers     case DATALINK_CLOSED:
7103b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
7113b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
7123b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
7139ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
714565e35e5SBrian Somers       dl->reconnect_tries =
7156f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
716565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
717c5a5a6caSBrian Somers       dl->script.run = runscripts;
718c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
719c7cc5030SBrian Somers       break;
720c7cc5030SBrian Somers 
721c7cc5030SBrian Somers     case DATALINK_OPENING:
722c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
723c7cc5030SBrian Somers         dl->script.run = 1;
724c7cc5030SBrian Somers       /* fall through */
725c7cc5030SBrian Somers 
726c7cc5030SBrian Somers     case DATALINK_DIAL:
727c7cc5030SBrian Somers     case DATALINK_LOGIN:
728c7cc5030SBrian Somers     case DATALINK_READY:
729c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
730c7cc5030SBrian Somers         dl->script.packetmode = 1;
731c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
732c7cc5030SBrian Somers           datalink_LoginDone(dl);
733c7cc5030SBrian Somers       }
734c7cc5030SBrian Somers       break;
7353006ec67SBrian Somers   }
7363006ec67SBrian Somers }
7373006ec67SBrian Somers 
7383006ec67SBrian Somers void
7399c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
740c7cc5030SBrian Somers {
741c7cc5030SBrian Somers   /* Please close */
742e2ebb036SBrian Somers   switch (dl->state) {
743e2ebb036SBrian Somers     case DATALINK_OPEN:
744643f4904SBrian Somers       peerid_Init(&dl->peer);
74509206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
746e2ebb036SBrian Somers       /* fall through */
747e2ebb036SBrian Somers 
748e2ebb036SBrian Somers     case DATALINK_AUTH:
749e2ebb036SBrian Somers     case DATALINK_LCP:
750dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
7519c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
752c7cc5030SBrian Somers         dl->dial_tries = -1;
753c7cc5030SBrian Somers         dl->reconnect_tries = 0;
7549c81b87dSBrian Somers         if (how == CLOSE_LCP)
7559c81b87dSBrian Somers           dl->stayonline = 1;
756c7cc5030SBrian Somers       }
757e2ebb036SBrian Somers       break;
758e2ebb036SBrian Somers 
759e2ebb036SBrian Somers     default:
7609c81b87dSBrian Somers       datalink_ComeDown(dl, how);
761c7cc5030SBrian Somers   }
762e2ebb036SBrian Somers }
763c7cc5030SBrian Somers 
764c7cc5030SBrian Somers void
7659c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
766c7cc5030SBrian Somers {
767c7cc5030SBrian Somers   /* Carrier is lost */
768e2ebb036SBrian Somers   switch (dl->state) {
769e2ebb036SBrian Somers     case DATALINK_OPEN:
770643f4904SBrian Somers       peerid_Init(&dl->peer);
77109206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
772e2ebb036SBrian Somers       /* fall through */
773e2ebb036SBrian Somers 
774e2ebb036SBrian Somers     case DATALINK_AUTH:
775e2ebb036SBrian Somers     case DATALINK_LCP:
77609206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
777e2ebb036SBrian Somers       /* fall through */
778c7cc5030SBrian Somers 
779e2ebb036SBrian Somers     default:
7809c81b87dSBrian Somers       datalink_ComeDown(dl, how);
781c7cc5030SBrian Somers   }
782e2ebb036SBrian Somers }
783c7cc5030SBrian Somers 
784c7cc5030SBrian Somers void
7853006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
7863006ec67SBrian Somers {
7873006ec67SBrian Somers   dl->reconnect_tries = 0;
7883006ec67SBrian Somers }
789c7cc5030SBrian Somers 
7909c81b87dSBrian Somers void
7919c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
7929c81b87dSBrian Somers {
7937729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
7949c81b87dSBrian Somers     dl->stayonline = 1;
7959c81b87dSBrian Somers }
7969c81b87dSBrian Somers 
797643f4904SBrian Somers int
798643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
799c7cc5030SBrian Somers {
800643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
801643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:            %s\n",
802643f4904SBrian Somers                 datalink_State(arg->cx));
803643f4904SBrian Somers   prompt_Printf(arg->prompt, " CHAP Encryption:  %s\n",
804643f4904SBrian Somers                 arg->cx->chap.using_MSChap ? "MSChap" : "MD5" );
805643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:        ");
806643f4904SBrian Somers   if (*arg->cx->peer.authname)
807643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
808643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
809643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
810565e35e5SBrian Somers   else
811643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
812643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:    %s\n",
813643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
814643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
815643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
816643f4904SBrian Somers 
817643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
818643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:       %s\n",
819643f4904SBrian Somers                 arg->cx->cfg.phone.list);
820643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
821643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:       %d, delay ",
822643f4904SBrian Somers                   arg->cx->cfg.dial.max);
823565e35e5SBrian Somers   else
824643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:       infinite, delay ");
825643f4904SBrian Somers   if (arg->cx->cfg.dial.next_timeout > 0)
826643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
827565e35e5SBrian Somers   else
828643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
829643f4904SBrian Somers   if (arg->cx->cfg.dial.timeout > 0)
830643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
831565e35e5SBrian Somers   else
832643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
833643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:  %d, delay ",
834643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
835643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
836643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
837643f4904SBrian Somers   else
838643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
839643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:      %s\n",
840643f4904SBrian Somers                 arg->cx->cfg.script.dial);
841643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:     %s\n",
842643f4904SBrian Somers                 arg->cx->cfg.script.login);
843643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:    %s\n",
844643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
845643f4904SBrian Somers   return 0;
846565e35e5SBrian Somers }
847565e35e5SBrian Somers 
848565e35e5SBrian Somers int
849565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
850565e35e5SBrian Somers {
85125092092SBrian Somers   if (arg->argc == arg->argn+2) {
85225092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
85325092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
854565e35e5SBrian Somers     return 0;
855565e35e5SBrian Somers   }
856565e35e5SBrian Somers   return -1;
857565e35e5SBrian Somers }
858565e35e5SBrian Somers 
859565e35e5SBrian Somers int
860565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
861565e35e5SBrian Somers {
862565e35e5SBrian Somers   int timeout;
863565e35e5SBrian Somers   int tries;
864565e35e5SBrian Somers   char *dot;
865565e35e5SBrian Somers 
86625092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
86725092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
86825092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
869565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
870565e35e5SBrian Somers       randinit();
871565e35e5SBrian Somers     } else {
87225092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
873565e35e5SBrian Somers 
874565e35e5SBrian Somers       if (timeout >= 0)
875565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
876565e35e5SBrian Somers       else {
877dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
878565e35e5SBrian Somers 	return -1;
879565e35e5SBrian Somers       }
880565e35e5SBrian Somers     }
881565e35e5SBrian Somers 
88225092092SBrian Somers     dot = strchr(arg->argv[arg->argn], '.');
883565e35e5SBrian Somers     if (dot) {
884565e35e5SBrian Somers       if (strcasecmp(++dot, "random") == 0) {
885565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
886565e35e5SBrian Somers 	randinit();
887565e35e5SBrian Somers       } else {
888565e35e5SBrian Somers 	timeout = atoi(dot);
889565e35e5SBrian Somers 	if (timeout >= 0)
890565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
891565e35e5SBrian Somers 	else {
892dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
893565e35e5SBrian Somers 	  return -1;
894565e35e5SBrian Somers 	}
895565e35e5SBrian Somers       }
896565e35e5SBrian Somers     } else
897565e35e5SBrian Somers       /* Default next timeout */
898565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
899565e35e5SBrian Somers 
90025092092SBrian Somers     if (arg->argc == arg->argn+2) {
90125092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
902565e35e5SBrian Somers 
903565e35e5SBrian Somers       if (tries >= 0) {
904565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
905565e35e5SBrian Somers       } else {
906dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
907565e35e5SBrian Somers 	return 1;
908565e35e5SBrian Somers       }
909565e35e5SBrian Somers     }
910565e35e5SBrian Somers     return 0;
911565e35e5SBrian Somers   }
912565e35e5SBrian Somers   return -1;
913c7cc5030SBrian Somers }
914c7cc5030SBrian Somers 
915cdbbb6b5SBrian Somers static const char *states[] = {
916565e35e5SBrian Somers   "closed",
917565e35e5SBrian Somers   "opening",
918565e35e5SBrian Somers   "hangup",
919565e35e5SBrian Somers   "dial",
920565e35e5SBrian Somers   "login",
921565e35e5SBrian Somers   "ready",
922565e35e5SBrian Somers   "lcp",
923565e35e5SBrian Somers   "auth",
924565e35e5SBrian Somers   "open"
925c7cc5030SBrian Somers };
926c7cc5030SBrian Somers 
927643f4904SBrian Somers const char *
928c7cc5030SBrian Somers datalink_State(struct datalink *dl)
929c7cc5030SBrian Somers {
930c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
931c7cc5030SBrian Somers     return "unknown";
932c7cc5030SBrian Somers   return states[dl->state];
933c7cc5030SBrian Somers }
9346f384573SBrian Somers 
9359ae58882SBrian Somers static void
9369ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
9379ae58882SBrian Somers {
9389ae58882SBrian Somers   if (state != dl->state) {
9399ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
9409ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
9419ae58882SBrian Somers                  states[state]);
9429ae58882SBrian Somers       dl->state = state;
9439ae58882SBrian Somers     } else
9449ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
9459ae58882SBrian Somers   }
9469ae58882SBrian Somers }
9479ae58882SBrian Somers 
9486f384573SBrian Somers struct datalink *
94996c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
95096c9bb21SBrian Somers              int fd)
9516f384573SBrian Somers {
952b7c5748eSBrian Somers   struct datalink *dl, *cdl;
9536f384573SBrian Somers   u_int retry;
954b7c5748eSBrian Somers   char *oname;
9556f384573SBrian Somers 
95696c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
95796c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
9586f384573SBrian Somers 
95996c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
96096c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
96196c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
96296c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
9636f384573SBrian Somers   }
964b7c5748eSBrian Somers 
965b7c5748eSBrian Somers   /* Make sure the name is unique ! */
966b7c5748eSBrian Somers   oname = NULL;
967b7c5748eSBrian Somers   do {
968b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
969b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
970b7c5748eSBrian Somers         if (oname)
971b7c5748eSBrian Somers           free(datalink_NextName(dl));
972b7c5748eSBrian Somers         else
973b7c5748eSBrian Somers           oname = datalink_NextName(dl);
974b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
975b7c5748eSBrian Somers       }
976b7c5748eSBrian Somers   } while (cdl);
977b7c5748eSBrian Somers 
978b7c5748eSBrian Somers   if (oname) {
979b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
980b7c5748eSBrian Somers     free(oname);
981b7c5748eSBrian Somers   } else {
98296c9bb21SBrian Somers     dl->name = strdup(dl->name);
983b7c5748eSBrian Somers     free(iov[*niov].iov_base);
984b7c5748eSBrian Somers   }
985b7c5748eSBrian Somers   (*niov)++;
9866f384573SBrian Somers 
9876f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
9886f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
9896f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
9906f384573SBrian Somers   dl->desc.Read = datalink_Read;
9916f384573SBrian Somers   dl->desc.Write = datalink_Write;
9926f384573SBrian Somers 
9936f384573SBrian Somers   mp_linkInit(&dl->mp);
9946f384573SBrian Somers   *dl->phone.list = '\0';
9956f384573SBrian Somers   dl->phone.next = NULL;
9966f384573SBrian Somers   dl->phone.alt = NULL;
9976f384573SBrian Somers   dl->phone.chosen = "N/A";
9986f384573SBrian Somers 
9996f384573SBrian Somers   dl->bundle = bundle;
10006f384573SBrian Somers   dl->next = NULL;
10016f384573SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
10026f384573SBrian Somers   dl->dial_tries = 0;
10036f384573SBrian Somers   dl->reconnect_tries = 0;
10046f384573SBrian Somers   dl->parent = &bundle->fsm;
10056f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
10066f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
10076f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
10086f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
10096f384573SBrian Somers   dl->fsmp.object = dl;
10106f384573SBrian Somers 
10116f384573SBrian Somers   retry = dl->pap.cfg.fsmretry;
1012dd7e2610SBrian Somers   auth_Init(&dl->pap);
10136f384573SBrian Somers   dl->pap.cfg.fsmretry = retry;
10146f384573SBrian Somers 
10156f384573SBrian Somers   retry = dl->chap.auth.cfg.fsmretry;
1016dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
10176f384573SBrian Somers   dl->chap.auth.cfg.fsmretry = retry;
10186f384573SBrian Somers 
101996c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
102096c9bb21SBrian Somers 
102196c9bb21SBrian Somers   if (!dl->physical) {
10226f384573SBrian Somers     free(dl->name);
10236f384573SBrian Somers     free(dl);
10246f384573SBrian Somers     dl = NULL;
10259ae58882SBrian Somers   } else {
10266f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
10276f384573SBrian Somers 
10289ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
10299ae58882SBrian Somers               dl->name, datalink_State(dl));
10309ae58882SBrian Somers   }
10319ae58882SBrian Somers 
10326f384573SBrian Somers   return dl;
10336f384573SBrian Somers }
10346f384573SBrian Somers 
10356f384573SBrian Somers int
103685fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
103785fd273aSBrian Somers              pid_t newpid)
10386f384573SBrian Somers {
103996c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
104096c9bb21SBrian Somers   int link_fd;
10416f384573SBrian Somers 
104296c9bb21SBrian Somers   if (dl) {
1043dd7e2610SBrian Somers     timer_Stop(&dl->dial_timer);
1044dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1045dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
10466f384573SBrian Somers   }
10476f384573SBrian Somers 
104896c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
104996c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
105096c9bb21SBrian Somers     if (dl) {
10516f384573SBrian Somers       free(dl->name);
10526f384573SBrian Somers       free(dl);
105396c9bb21SBrian Somers     }
105496c9bb21SBrian Somers     return -1;
105596c9bb21SBrian Somers   }
105696c9bb21SBrian Somers 
105796c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
105896c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
105996c9bb21SBrian Somers   iov[*niov].iov_base =
106096c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
106196c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
106296c9bb21SBrian Somers 
106385fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
106496c9bb21SBrian Somers 
106596c9bb21SBrian Somers   if (link_fd == -1 && dl) {
106696c9bb21SBrian Somers     free(dl->name);
106796c9bb21SBrian Somers     free(dl);
106896c9bb21SBrian Somers   }
10696f384573SBrian Somers 
10706f384573SBrian Somers   return link_fd;
10716f384573SBrian Somers }
10726f384573SBrian Somers 
107358d55334SBrian Somers void
107458d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
107558d55334SBrian Somers {
107658d55334SBrian Somers   free(dl->name);
107758d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
107858d55334SBrian Somers }
107958d55334SBrian Somers 
108084917b87SBrian Somers char *
108184917b87SBrian Somers datalink_NextName(struct datalink *dl)
10826f384573SBrian Somers {
10836f384573SBrian Somers   int f, n;
108484917b87SBrian Somers   char *name, *oname;
10856f384573SBrian Somers 
10866f384573SBrian Somers   n = strlen(dl->name);
10876f384573SBrian Somers   name = (char *)malloc(n+3);
10886f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
10896f384573SBrian Somers     if (!isdigit(dl->name[f]))
10906f384573SBrian Somers       break;
10916f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
10926f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
109384917b87SBrian Somers   oname = dl->name;
109454cd8e13SBrian Somers   dl->name = name;
109554cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
109684917b87SBrian Somers   return oname;
10976f384573SBrian Somers }
1098dd0645c5SBrian Somers 
1099dd0645c5SBrian Somers int
1100dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1101dd0645c5SBrian Somers {
1102dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1103dd0645c5SBrian Somers     return 0;
1104dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1105dd0645c5SBrian Somers     dl->script.run = 0;
1106dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1107dd0645c5SBrian Somers     dl->reconnect_tries = 0;
110881358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1109dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1110dd0645c5SBrian Somers   return 1;
1111dd0645c5SBrian Somers }
1112