xref: /freebsd/usr.sbin/ppp/datalink.c (revision b51a60cc)
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  *
26b51a60ccSBrian Somers  *	$Id: datalink.c,v 1.24 1999/01/12 21:50:20 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"
7092b09558SBrian Somers #include "cbcp.h"
71e2ebb036SBrian Somers #include "datalink.h"
72c7cc5030SBrian Somers 
73030e4ebbSBrian Somers static void datalink_LoginDone(struct datalink *);
749ae58882SBrian Somers static void datalink_NewState(struct datalink *, int);
753006ec67SBrian Somers 
763006ec67SBrian Somers static void
773006ec67SBrian Somers datalink_OpenTimeout(void *v)
783006ec67SBrian Somers {
793006ec67SBrian Somers   struct datalink *dl = (struct datalink *)v;
803006ec67SBrian Somers 
81dd7e2610SBrian Somers   timer_Stop(&dl->dial_timer);
823006ec67SBrian Somers   if (dl->state == DATALINK_OPENING)
83dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
843006ec67SBrian Somers }
853006ec67SBrian Somers 
863006ec67SBrian Somers static void
873006ec67SBrian Somers datalink_StartDialTimer(struct datalink *dl, int Timeout)
883006ec67SBrian Somers {
89dd7e2610SBrian Somers   timer_Stop(&dl->dial_timer);
903006ec67SBrian Somers 
913006ec67SBrian Somers   if (Timeout) {
923006ec67SBrian Somers     if (Timeout > 0)
933006ec67SBrian Somers       dl->dial_timer.load = Timeout * SECTICKS;
943006ec67SBrian Somers     else
95e718d1d7SBrian Somers       dl->dial_timer.load = (random() % DIAL_TIMEOUT) * SECTICKS;
963006ec67SBrian Somers     dl->dial_timer.func = datalink_OpenTimeout;
973b0f8d2eSBrian Somers     dl->dial_timer.name = "dial";
983006ec67SBrian Somers     dl->dial_timer.arg = dl;
99dd7e2610SBrian Somers     timer_Start(&dl->dial_timer);
1003006ec67SBrian Somers     if (dl->state == DATALINK_OPENING)
101dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
1023006ec67SBrian Somers                 dl->name, Timeout);
1033006ec67SBrian Somers   }
1043006ec67SBrian Somers }
1053006ec67SBrian Somers 
1063006ec67SBrian Somers static void
1073006ec67SBrian Somers datalink_HangupDone(struct datalink *dl)
1083006ec67SBrian Somers {
109030e4ebbSBrian Somers   if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
110dd7e2610SBrian Somers       physical_GetFD(dl->physical) != -1) {
111030e4ebbSBrian Somers     /* Don't close our modem if the link is dedicated */
112030e4ebbSBrian Somers     datalink_LoginDone(dl);
113030e4ebbSBrian Somers     return;
114030e4ebbSBrian Somers   }
115030e4ebbSBrian Somers 
1163006ec67SBrian Somers   modem_Close(dl->physical);
117565e35e5SBrian Somers   dl->phone.chosen = "N/A";
1183006ec67SBrian Somers 
11992b09558SBrian Somers   if (dl->cbcp.required) {
12092b09558SBrian Somers     log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
12192b09558SBrian Somers     dl->cfg.callback.opmask = 0;
12292b09558SBrian Somers     strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
12392b09558SBrian Somers             sizeof dl->cfg.phone.list - 1);
12492b09558SBrian Somers     dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
12592b09558SBrian Somers     dl->phone.alt = dl->phone.next = NULL;
12692b09558SBrian Somers     dl->reconnect_tries = dl->cfg.reconnect.max;
12792b09558SBrian Somers     dl->dial_tries = dl->cfg.dial.max;
12892b09558SBrian Somers     dl->script.run = 1;
12992b09558SBrian Somers     dl->script.packetmode = 1;
13092b09558SBrian Somers     if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
13192b09558SBrian Somers       log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
13292b09558SBrian Somers     bundle_LinksRemoved(dl->bundle);
13392b09558SBrian Somers     if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
13492b09558SBrian Somers       dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
13592b09558SBrian Somers     datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
13692b09558SBrian Somers     cbcp_Down(&dl->cbcp);
13792b09558SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
13892b09558SBrian Somers   } else if (dl->bundle->CleaningUp ||
1396f384573SBrian Somers       (dl->physical->type == PHYS_DIRECT) ||
1403b0f8d2eSBrian Somers       ((!dl->dial_tries || (dl->dial_tries < 0 && !dl->reconnect_tries)) &&
14181358fa3SBrian Somers        !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
1429ae58882SBrian Somers     datalink_NewState(dl, DATALINK_CLOSED);
1433006ec67SBrian Somers     dl->dial_tries = -1;
1443006ec67SBrian Somers     dl->reconnect_tries = 0;
1453006ec67SBrian Somers     bundle_LinkClosed(dl->bundle, dl);
1463b0f8d2eSBrian Somers     if (!dl->bundle->CleaningUp)
147565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
1483006ec67SBrian Somers   } else {
1499ae58882SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
150abff9baeSBrian Somers     if (dl->dial_tries < 0) {
151565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
152565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
153abff9baeSBrian Somers       dl->reconnect_tries--;
154abff9baeSBrian Somers     } else {
155f9545805SBrian Somers       if (dl->phone.next == NULL)
156565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
1573006ec67SBrian Somers       else
158565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
1593006ec67SBrian Somers     }
160abff9baeSBrian Somers   }
161abff9baeSBrian Somers }
1623006ec67SBrian Somers 
163f9545805SBrian Somers static const char *
164f9545805SBrian Somers datalink_ChoosePhoneNumber(struct datalink *dl)
165f9545805SBrian Somers {
166f9545805SBrian Somers   char *phone;
167f9545805SBrian Somers 
168f9545805SBrian Somers   if (dl->phone.alt == NULL) {
169f9545805SBrian Somers     if (dl->phone.next == NULL) {
170f9545805SBrian Somers       strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
171f9545805SBrian Somers       dl->phone.list[sizeof dl->phone.list - 1] = '\0';
172f9545805SBrian Somers       dl->phone.next = dl->phone.list;
173f9545805SBrian Somers     }
174f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
175f9545805SBrian Somers   }
176f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
177f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
178f9545805SBrian Somers   if (*phone)
179dd7e2610SBrian Somers     log_Printf(LogPHASE, "Phone: %s\n", phone);
180f9545805SBrian Somers   return phone;
181f9545805SBrian Somers }
182f9545805SBrian Somers 
183c5a5a6caSBrian Somers static void
184c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
185c5a5a6caSBrian Somers {
186d345321bSBrian Somers   if (!dl->script.packetmode) {
187d345321bSBrian Somers     dl->dial_tries = -1;
1889ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
189d345321bSBrian Somers   } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
190c5a5a6caSBrian Somers     dl->dial_tries = 0;
191dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
192c5a5a6caSBrian Somers     if (dl->script.run) {
1939ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
194c5a5a6caSBrian Somers       modem_Offline(dl->physical);
195f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
196030e4ebbSBrian Somers     } else {
197d4af231cSBrian Somers       timer_Stop(&dl->physical->Timer);
198030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
199030e4ebbSBrian Somers         /* force a redial timeout */
200030e4ebbSBrian Somers         modem_Close(dl->physical);
201c5a5a6caSBrian Somers       datalink_HangupDone(dl);
202030e4ebbSBrian Somers     }
203c5a5a6caSBrian Somers   } else {
204c7cc5030SBrian Somers     dl->dial_tries = -1;
205c7cc5030SBrian Somers 
206643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
2073b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
2083b0f8d2eSBrian Somers 
209cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
210cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
2113b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
212503a7782SBrian Somers 
2139ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
214dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
215dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
216c5a5a6caSBrian Somers   }
217c5a5a6caSBrian Somers }
218c5a5a6caSBrian Somers 
2193006ec67SBrian Somers static int
2203006ec67SBrian Somers datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
2213006ec67SBrian Somers                    int *n)
2223006ec67SBrian Somers {
2233006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2243006ec67SBrian Somers   int result;
2253006ec67SBrian Somers 
226310c3babSBrian Somers   result = 0;
2273006ec67SBrian Somers   switch (dl->state) {
2283006ec67SBrian Somers     case DATALINK_CLOSED:
22981358fa3SBrian Somers       if ((dl->physical->type &
23081358fa3SBrian Somers            (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
231565e35e5SBrian Somers           !bundle_IsDead(dl->bundle))
232565e35e5SBrian Somers         /*
23381358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
23481358fa3SBrian Somers          * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
23581358fa3SBrian Somers          * Go to DATALINK_OPENING via datalink_Up() and fall through.
236565e35e5SBrian Somers          */
237565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
238565e35e5SBrian Somers       else
239310c3babSBrian Somers         break;
240565e35e5SBrian Somers       /* fall through */
2413006ec67SBrian Somers 
2423006ec67SBrian Somers     case DATALINK_OPENING:
2433006ec67SBrian Somers       if (dl->dial_timer.state != TIMER_RUNNING) {
2443006ec67SBrian Somers         if (--dl->dial_tries < 0)
2453006ec67SBrian Somers           dl->dial_tries = 0;
2463006ec67SBrian Somers         if (modem_Open(dl->physical, dl->bundle) >= 0) {
247bf1d3ff6SBrian Somers           log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
248bf1d3ff6SBrian Somers                            "Type `~?' for help\r\n", dl->name,
249bf1d3ff6SBrian Somers                            dl->physical->name.full);
250c5a5a6caSBrian Somers           if (dl->script.run) {
2519ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
252f9545805SBrian Somers             chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
253f9545805SBrian Somers                       datalink_ChoosePhoneNumber(dl));
25481358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
255565e35e5SBrian Somers                 dl->cfg.dial.max)
256dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
257565e35e5SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial_tries,
258565e35e5SBrian Somers                         dl->cfg.dial.max);
259c5a5a6caSBrian Somers           } else
260c5a5a6caSBrian Somers             datalink_LoginDone(dl);
2618b09cf1cSBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
2623006ec67SBrian Somers         } else {
26381358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
264565e35e5SBrian Somers               dl->cfg.dial.max)
265dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
266565e35e5SBrian Somers                        dl->cfg.dial.max - dl->dial_tries, dl->cfg.dial.max);
2673006ec67SBrian Somers           else
268dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem\n");
2693006ec67SBrian Somers 
2703b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
27181358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
272565e35e5SBrian Somers                dl->cfg.dial.max && dl->dial_tries == 0)) {
2739ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
2743006ec67SBrian Somers             dl->reconnect_tries = 0;
2753006ec67SBrian Somers             dl->dial_tries = -1;
276bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s\n",
277bf1d3ff6SBrian Somers                              dl->physical->name.full);
2785b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
279c5a5a6caSBrian Somers           }
280bf1d3ff6SBrian Somers           if (!dl->bundle->CleaningUp) {
281bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
282bf1d3ff6SBrian Somers                              dl->physical->name.full, dl->cfg.dial.timeout);
283565e35e5SBrian Somers             datalink_StartDialTimer(dl, dl->cfg.dial.timeout);
2843006ec67SBrian Somers           }
2853006ec67SBrian Somers         }
286bf1d3ff6SBrian Somers       }
287310c3babSBrian Somers       break;
2883006ec67SBrian Somers 
2893006ec67SBrian Somers     case DATALINK_HANGUP:
2903006ec67SBrian Somers     case DATALINK_DIAL:
2913006ec67SBrian Somers     case DATALINK_LOGIN:
2923006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
2933006ec67SBrian Somers       switch (dl->chat.state) {
2943006ec67SBrian Somers         case CHAT_DONE:
2953006ec67SBrian Somers           /* script succeeded */
29639d94652SBrian Somers           chat_Destroy(&dl->chat);
2973006ec67SBrian Somers           switch(dl->state) {
2983006ec67SBrian Somers             case DATALINK_HANGUP:
2993006ec67SBrian Somers               datalink_HangupDone(dl);
3003006ec67SBrian Somers               break;
3013006ec67SBrian Somers             case DATALINK_DIAL:
3029ae58882SBrian Somers               datalink_NewState(dl, DATALINK_LOGIN);
303f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
3041342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3053006ec67SBrian Somers             case DATALINK_LOGIN:
30647dd77c1SBrian Somers               dl->phone.alt = NULL;
307c5a5a6caSBrian Somers               datalink_LoginDone(dl);
308b51a60ccSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3093006ec67SBrian Somers           }
3103006ec67SBrian Somers           break;
3113006ec67SBrian Somers         case CHAT_FAILED:
3123006ec67SBrian Somers           /* Going down - script failed */
313dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
31439d94652SBrian Somers           chat_Destroy(&dl->chat);
3153006ec67SBrian Somers           switch(dl->state) {
3163006ec67SBrian Somers             case DATALINK_HANGUP:
3173006ec67SBrian Somers               datalink_HangupDone(dl);
3183006ec67SBrian Somers               break;
3193006ec67SBrian Somers             case DATALINK_DIAL:
3203006ec67SBrian Somers             case DATALINK_LOGIN:
3219ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
322d4af231cSBrian Somers               modem_Offline(dl->physical);	/* Is this required ? */
323f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
3241342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3253006ec67SBrian Somers           }
3263006ec67SBrian Somers           break;
3273006ec67SBrian Somers       }
3283006ec67SBrian Somers       break;
329c7cc5030SBrian Somers 
330c7cc5030SBrian Somers     case DATALINK_READY:
331e2ebb036SBrian Somers     case DATALINK_LCP:
332e2ebb036SBrian Somers     case DATALINK_AUTH:
33392b09558SBrian Somers     case DATALINK_CBCP:
3343006ec67SBrian Somers     case DATALINK_OPEN:
3353006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3363006ec67SBrian Somers       break;
3373006ec67SBrian Somers   }
3383006ec67SBrian Somers   return result;
3393006ec67SBrian Somers }
3403006ec67SBrian Somers 
341ea722969SBrian Somers int
342ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
343ea722969SBrian Somers {
344ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
345ea722969SBrian Somers }
346ea722969SBrian Somers 
3473006ec67SBrian Somers static int
3482f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3493006ec67SBrian Somers {
3503006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3513006ec67SBrian Somers 
3523006ec67SBrian Somers   switch (dl->state) {
3533006ec67SBrian Somers     case DATALINK_CLOSED:
354c7cc5030SBrian Somers     case DATALINK_OPENING:
3553006ec67SBrian Somers       break;
356c7cc5030SBrian Somers 
3573006ec67SBrian Somers     case DATALINK_HANGUP:
3583006ec67SBrian Somers     case DATALINK_DIAL:
3593006ec67SBrian Somers     case DATALINK_LOGIN:
3603006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
361c7cc5030SBrian Somers 
362c7cc5030SBrian Somers     case DATALINK_READY:
363e2ebb036SBrian Somers     case DATALINK_LCP:
364e2ebb036SBrian Somers     case DATALINK_AUTH:
36592b09558SBrian Somers     case DATALINK_CBCP:
3663006ec67SBrian Somers     case DATALINK_OPEN:
3673006ec67SBrian Somers       return descriptor_IsSet(&dl->physical->desc, fdset);
3683006ec67SBrian Somers   }
3693006ec67SBrian Somers   return 0;
3703006ec67SBrian Somers }
3713006ec67SBrian Somers 
3723006ec67SBrian Somers static void
3733006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3743006ec67SBrian Somers {
3753006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3763006ec67SBrian Somers 
3773006ec67SBrian Somers   switch (dl->state) {
3783006ec67SBrian Somers     case DATALINK_CLOSED:
379c7cc5030SBrian Somers     case DATALINK_OPENING:
3803006ec67SBrian Somers       break;
381c7cc5030SBrian Somers 
3823006ec67SBrian Somers     case DATALINK_HANGUP:
3833006ec67SBrian Somers     case DATALINK_DIAL:
3843006ec67SBrian Somers     case DATALINK_LOGIN:
3853006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
3863006ec67SBrian Somers       break;
387c7cc5030SBrian Somers 
388c7cc5030SBrian Somers     case DATALINK_READY:
389e2ebb036SBrian Somers     case DATALINK_LCP:
390e2ebb036SBrian Somers     case DATALINK_AUTH:
39192b09558SBrian Somers     case DATALINK_CBCP:
3923006ec67SBrian Somers     case DATALINK_OPEN:
3933006ec67SBrian Somers       descriptor_Read(&dl->physical->desc, bundle, fdset);
3943006ec67SBrian Somers       break;
3953006ec67SBrian Somers   }
3963006ec67SBrian Somers }
3973006ec67SBrian Somers 
3981af29a6eSBrian Somers static int
399f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
4003006ec67SBrian Somers {
4013006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4021af29a6eSBrian Somers   int result = 0;
4033006ec67SBrian Somers 
4043006ec67SBrian Somers   switch (dl->state) {
4053006ec67SBrian Somers     case DATALINK_CLOSED:
406c7cc5030SBrian Somers     case DATALINK_OPENING:
4073006ec67SBrian Somers       break;
408c7cc5030SBrian Somers 
4093006ec67SBrian Somers     case DATALINK_HANGUP:
4103006ec67SBrian Somers     case DATALINK_DIAL:
4113006ec67SBrian Somers     case DATALINK_LOGIN:
4121af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4133006ec67SBrian Somers       break;
414c7cc5030SBrian Somers 
415c7cc5030SBrian Somers     case DATALINK_READY:
416e2ebb036SBrian Somers     case DATALINK_LCP:
417e2ebb036SBrian Somers     case DATALINK_AUTH:
41892b09558SBrian Somers     case DATALINK_CBCP:
4193006ec67SBrian Somers     case DATALINK_OPEN:
4201af29a6eSBrian Somers       result = descriptor_Write(&dl->physical->desc, bundle, fdset);
4213006ec67SBrian Somers       break;
4223006ec67SBrian Somers   }
4231af29a6eSBrian Somers 
4241af29a6eSBrian Somers   return result;
4253006ec67SBrian Somers }
4263006ec67SBrian Somers 
4276d666775SBrian Somers static void
4289c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
429e2ebb036SBrian Somers {
4309c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
431e2ebb036SBrian Somers     dl->dial_tries = -1;
432e2ebb036SBrian Somers     dl->reconnect_tries = 0;
4337729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4349c81b87dSBrian Somers       dl->stayonline = 1;
435e2ebb036SBrian Somers   }
436e2ebb036SBrian Somers 
4377729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4389c81b87dSBrian Somers     dl->stayonline = 0;
439d4af231cSBrian Somers     timer_Stop(&dl->physical->Timer);
4409c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4419c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
442e2ebb036SBrian Somers     modem_Offline(dl->physical);
443b6f5f442SBrian Somers     chat_Destroy(&dl->chat);
444e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4459ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
446f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
447e2ebb036SBrian Somers     } else
448e2ebb036SBrian Somers       datalink_HangupDone(dl);
449e2ebb036SBrian Somers   }
450e2ebb036SBrian Somers }
451e2ebb036SBrian Somers 
452e2ebb036SBrian Somers static void
4536d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4546d666775SBrian Somers {
4556d666775SBrian Somers   /* The given FSM is about to start up ! */
4566d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
457a611cad6SBrian Somers 
45892e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
459e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
460e2ebb036SBrian Somers }
4616d666775SBrian Somers 
4626d666775SBrian Somers static void
4636d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4646d666775SBrian Somers {
4656d666775SBrian Somers   /* The given fsm is now up */
4666d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4676d666775SBrian Somers 
46892e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
469643f4904SBrian Somers     datalink_GotAuthname(dl, "", 0);
4703b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth;
4713b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth;
4723b0f8d2eSBrian Somers     if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) {
4733b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH)
4745563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
475dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
4763b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.his_auth),
4773b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.want_auth));
4783b0f8d2eSBrian Somers       if (dl->physical->link.lcp.his_auth == PROTO_PAP)
479dd7e2610SBrian Somers         auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge);
4803b0f8d2eSBrian Somers       if (dl->physical->link.lcp.want_auth == PROTO_CHAP)
481dd7e2610SBrian Somers         auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge);
482e2ebb036SBrian Somers     } else
483e2ebb036SBrian Somers       datalink_AuthOk(dl);
484e2ebb036SBrian Somers   }
485e2ebb036SBrian Somers }
486e2ebb036SBrian Somers 
487e2ebb036SBrian Somers void
488643f4904SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name, int len)
489643f4904SBrian Somers {
490643f4904SBrian Somers   if (len >= sizeof dl->peer.authname)
491643f4904SBrian Somers     len = sizeof dl->peer.authname - 1;
492643f4904SBrian Somers   strncpy(dl->peer.authname, name, len);
493643f4904SBrian Somers   dl->peer.authname[len] = '\0';
494643f4904SBrian Somers }
495643f4904SBrian Somers 
496643f4904SBrian Somers void
49792b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
498e2ebb036SBrian Somers {
49906337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5001df0a3b9SBrian Somers 
501dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5021fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5031fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5041fa665f5SBrian Somers       case MP_LINKSENT:
505ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5061fa665f5SBrian Somers         return;
5071fa665f5SBrian Somers       case MP_UP:
5080a1b5c9dSBrian Somers         /* First link in the bundle */
50992b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
5100a1b5c9dSBrian Somers         /* fall through */
5111fa665f5SBrian Somers       case MP_ADDED:
5120a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5131df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
5141fa665f5SBrian Somers         break;
5151fa665f5SBrian Somers       case MP_FAILED:
51649052c95SBrian Somers         datalink_AuthNotOk(dl);
51749052c95SBrian Somers         return;
51849052c95SBrian Somers     }
51949052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
520dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
521897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
522897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
52349052c95SBrian Somers     return;
52450abd4c8SBrian Somers   } else {
52550abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
526ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
52792b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
52850abd4c8SBrian Somers   }
52949052c95SBrian Somers 
53006337856SBrian Somers   if (ccpok) {
531dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
532dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
53306337856SBrian Somers   }
5349ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5353b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5363b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5376d666775SBrian Somers }
538e2ebb036SBrian Somers 
539e2ebb036SBrian Somers void
54092b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
54192b09558SBrian Somers {
54292b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
54392b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
54492b09558SBrian Somers }
54592b09558SBrian Somers 
54692b09558SBrian Somers void
54792b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
54892b09558SBrian Somers {
54992b09558SBrian Somers   cbcp_Down(&dl->cbcp);
55092b09558SBrian Somers   datalink_CBCPComplete(dl);
55192b09558SBrian Somers }
55292b09558SBrian Somers 
55392b09558SBrian Somers void
55492b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
55592b09558SBrian Somers {
5565165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
55792b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
5585165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
5595165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
5605165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
5615165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
5625165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
56392b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
56492b09558SBrian Somers     cbcp_Up(&dl->cbcp);
56592b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
5665165af6fSBrian Somers     /* It's not CBCP */
56792b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
56892b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
56992b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
57092b09558SBrian Somers   } else
57192b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
57292b09558SBrian Somers       case 0:
57392b09558SBrian Somers         datalink_NCPUp(dl);
57492b09558SBrian Somers         break;
57592b09558SBrian Somers 
57692b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
57792b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
57892b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
57992b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
58092b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
58192b09558SBrian Somers                      dl->peer.authname);
58292b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
58392b09558SBrian Somers         } else {
58492b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
58592b09558SBrian Somers           if (ptr)
58692b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
58792b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
58892b09558SBrian Somers                      dl->cbcp.fsm.phone);
58992b09558SBrian Somers           dl->cbcp.required = 1;
59092b09558SBrian Somers         }
59192b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
59292b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
59392b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
59492b09558SBrian Somers         break;
59592b09558SBrian Somers 
59692b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
59792b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
59892b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
59992b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
60092b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
60192b09558SBrian Somers                    dl->cbcp.fsm.phone);
60292b09558SBrian Somers         dl->cbcp.required = 1;
60392b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
60492b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
60592b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
60692b09558SBrian Somers         break;
60792b09558SBrian Somers 
60892b09558SBrian Somers       default:
60992b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
61092b09558SBrian Somers                    dl->name);
61192b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
61292b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
61392b09558SBrian Somers         break;
61492b09558SBrian Somers     }
61592b09558SBrian Somers }
61692b09558SBrian Somers 
61792b09558SBrian Somers void
618e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
619e2ebb036SBrian Somers {
6209ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
621dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6226d666775SBrian Somers }
6236d666775SBrian Somers 
6246d666775SBrian Somers static void
6256d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6266d666775SBrian Somers {
6276d666775SBrian Somers   /* The given FSM has been told to come down */
6286d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
629a611cad6SBrian Somers 
63092e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
631e2ebb036SBrian Somers     switch (dl->state) {
632e2ebb036SBrian Somers       case DATALINK_OPEN:
633643f4904SBrian Somers         peerid_Init(&dl->peer);
63409206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
635ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
636e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
63792b09558SBrian Somers         /* fall through (just in case) */
63892b09558SBrian Somers 
63992b09558SBrian Somers       case DATALINK_CBCP:
64092b09558SBrian Somers         if (!dl->cbcp.required)
64192b09558SBrian Somers           cbcp_Down(&dl->cbcp);
64292b09558SBrian Somers         /* fall through (just in case) */
643e2ebb036SBrian Somers 
644e2ebb036SBrian Somers       case DATALINK_AUTH:
645dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
646dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
6476d666775SBrian Somers     }
6489ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
649e2ebb036SBrian Somers   }
6506d666775SBrian Somers }
6516d666775SBrian Somers 
6526d666775SBrian Somers static void
6536d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
6546d666775SBrian Somers {
6556d666775SBrian Somers   /* The given fsm is now down */
6566d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
6576d666775SBrian Somers 
65892e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
65909206a6fSBrian Somers     fsm2initial(fp);
6606d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
6619c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
6620a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
6630a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
6646d666775SBrian Somers }
6656d666775SBrian Somers 
6663006ec67SBrian Somers struct datalink *
6676f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
6683006ec67SBrian Somers {
6693006ec67SBrian Somers   struct datalink *dl;
6703006ec67SBrian Somers 
6713006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
6723006ec67SBrian Somers   if (dl == NULL)
6733006ec67SBrian Somers     return dl;
6743006ec67SBrian Somers 
6753006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
6763006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
6773006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
6783006ec67SBrian Somers   dl->desc.Read = datalink_Read;
6793006ec67SBrian Somers   dl->desc.Write = datalink_Write;
6805b8b8060SBrian Somers 
681e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
682e718d1d7SBrian Somers 
68373a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
68473a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
68573a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
686f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
687f9545805SBrian Somers   *dl->phone.list = '\0';
688f9545805SBrian Somers   dl->phone.next = NULL;
689f9545805SBrian Somers   dl->phone.alt = NULL;
690f9545805SBrian Somers   dl->phone.chosen = "N/A";
6919c81b87dSBrian Somers   dl->stayonline = 0;
692c7cc5030SBrian Somers   dl->script.run = 1;
693c7cc5030SBrian Somers   dl->script.packetmode = 1;
6943b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
6955b8b8060SBrian Somers 
6963006ec67SBrian Somers   dl->bundle = bundle;
6973006ec67SBrian Somers   dl->next = NULL;
698e718d1d7SBrian Somers 
6993006ec67SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
700e718d1d7SBrian Somers 
7013006ec67SBrian Somers   dl->dial_tries = 0;
702565e35e5SBrian Somers   dl->cfg.dial.max = 1;
703565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
704565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
705e718d1d7SBrian Somers 
706abff9baeSBrian Somers   dl->reconnect_tries = 0;
707565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
708565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
709abff9baeSBrian Somers 
71092b09558SBrian Somers   dl->cfg.callback.opmask = 0;
71192b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
71292b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
71392b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
71492b09558SBrian Somers 
7153006ec67SBrian Somers   dl->name = strdup(name);
716643f4904SBrian Somers   peerid_Init(&dl->peer);
7176f384573SBrian Somers   dl->parent = &bundle->fsm;
7183b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7193b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7203b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7213b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7223b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7233b0f8d2eSBrian Somers 
724dd7e2610SBrian Somers   auth_Init(&dl->pap);
725dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
7263b0f8d2eSBrian Somers 
727565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
7283006ec67SBrian Somers     free(dl->name);
7293006ec67SBrian Somers     free(dl);
7303006ec67SBrian Somers     return NULL;
7313006ec67SBrian Somers   }
73292b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
733f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
7343006ec67SBrian Somers 
7359ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
7369ae58882SBrian Somers              dl->name, datalink_State(dl));
7373006ec67SBrian Somers 
7383006ec67SBrian Somers   return dl;
7393006ec67SBrian Somers }
7403006ec67SBrian Somers 
7413006ec67SBrian Somers struct datalink *
742cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
743cd7bd93aSBrian Somers {
744cd7bd93aSBrian Somers   struct datalink *dl;
745cd7bd93aSBrian Somers 
746cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
747cd7bd93aSBrian Somers   if (dl == NULL)
748cd7bd93aSBrian Somers     return dl;
749cd7bd93aSBrian Somers 
750cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
751cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
752cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
753cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
754cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
755cd7bd93aSBrian Somers 
756cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
757cd7bd93aSBrian Somers 
758cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
759cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
760cd7bd93aSBrian Somers   *dl->phone.list = '\0';
761643f4904SBrian Somers   dl->phone.next = NULL;
762643f4904SBrian Somers   dl->phone.alt = NULL;
763643f4904SBrian Somers   dl->phone.chosen = "N/A";
764cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
765cd7bd93aSBrian Somers   dl->next = NULL;
766cd7bd93aSBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
767cd7bd93aSBrian Somers   dl->dial_tries = 0;
768cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
769cd7bd93aSBrian Somers   dl->name = strdup(name);
770643f4904SBrian Somers   peerid_Init(&dl->peer);
771cd7bd93aSBrian Somers   dl->parent = odl->parent;
772cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
773643f4904SBrian Somers   dl->fsmp.object = dl;
774dd7e2610SBrian Somers   auth_Init(&dl->pap);
775cd7bd93aSBrian Somers   dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry;
776cd7bd93aSBrian Somers 
777dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
778cd7bd93aSBrian Somers   dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry;
779cd7bd93aSBrian Somers 
78081358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
781cd7bd93aSBrian Somers     free(dl->name);
782cd7bd93aSBrian Somers     free(dl);
783cd7bd93aSBrian Somers     return NULL;
784cd7bd93aSBrian Somers   }
785cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
786cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
787cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
788cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
789cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
790cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
791cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
792cd7bd93aSBrian Somers 
79392b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
794cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
795cd7bd93aSBrian Somers 
7969ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
7979ae58882SBrian Somers              dl->name, datalink_State(dl));
798cd7bd93aSBrian Somers 
799cd7bd93aSBrian Somers   return dl;
800cd7bd93aSBrian Somers }
801cd7bd93aSBrian Somers 
802cd7bd93aSBrian Somers struct datalink *
8033006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
8043006ec67SBrian Somers {
8053006ec67SBrian Somers   struct datalink *result;
8063006ec67SBrian Somers 
80739d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
808dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
809c7cc5030SBrian Somers               datalink_State(dl));
81039d94652SBrian Somers     switch (dl->state) {
81139d94652SBrian Somers       case DATALINK_HANGUP:
81239d94652SBrian Somers       case DATALINK_DIAL:
81339d94652SBrian Somers       case DATALINK_LOGIN:
81439d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
81539d94652SBrian Somers         break;
81639d94652SBrian Somers     }
81739d94652SBrian Somers   }
8183006ec67SBrian Somers 
819f1e8dfb2SBrian Somers   timer_Stop(&dl->dial_timer);
8203006ec67SBrian Somers   result = dl->next;
8213b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
8223006ec67SBrian Somers   free(dl->name);
8233006ec67SBrian Somers   free(dl);
8243006ec67SBrian Somers 
8253006ec67SBrian Somers   return result;
8263006ec67SBrian Somers }
8273006ec67SBrian Somers 
8283006ec67SBrian Somers void
829c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8303006ec67SBrian Somers {
8316f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
832565e35e5SBrian Somers     /* Ignore scripts */
833565e35e5SBrian Somers     runscripts = 0;
834565e35e5SBrian Somers 
835c7cc5030SBrian Somers   switch (dl->state) {
836c7cc5030SBrian Somers     case DATALINK_CLOSED:
8373b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
8383b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
8393b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
8409ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
841565e35e5SBrian Somers       dl->reconnect_tries =
8426f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
843565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
844c5a5a6caSBrian Somers       dl->script.run = runscripts;
845c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
846c7cc5030SBrian Somers       break;
847c7cc5030SBrian Somers 
848c7cc5030SBrian Somers     case DATALINK_OPENING:
849c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
850c7cc5030SBrian Somers         dl->script.run = 1;
851c7cc5030SBrian Somers       /* fall through */
852c7cc5030SBrian Somers 
853c7cc5030SBrian Somers     case DATALINK_DIAL:
854c7cc5030SBrian Somers     case DATALINK_LOGIN:
855c7cc5030SBrian Somers     case DATALINK_READY:
856c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
857c7cc5030SBrian Somers         dl->script.packetmode = 1;
858c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
859c7cc5030SBrian Somers           datalink_LoginDone(dl);
860c7cc5030SBrian Somers       }
861c7cc5030SBrian Somers       break;
8623006ec67SBrian Somers   }
8633006ec67SBrian Somers }
8643006ec67SBrian Somers 
8653006ec67SBrian Somers void
8669c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
867c7cc5030SBrian Somers {
868c7cc5030SBrian Somers   /* Please close */
869e2ebb036SBrian Somers   switch (dl->state) {
870e2ebb036SBrian Somers     case DATALINK_OPEN:
871643f4904SBrian Somers       peerid_Init(&dl->peer);
87209206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
873e2ebb036SBrian Somers       /* fall through */
874e2ebb036SBrian Somers 
87592b09558SBrian Somers     case DATALINK_CBCP:
876e2ebb036SBrian Somers     case DATALINK_AUTH:
877e2ebb036SBrian Somers     case DATALINK_LCP:
878dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
8799c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
880c7cc5030SBrian Somers         dl->dial_tries = -1;
881c7cc5030SBrian Somers         dl->reconnect_tries = 0;
8829c81b87dSBrian Somers         if (how == CLOSE_LCP)
8839c81b87dSBrian Somers           dl->stayonline = 1;
884c7cc5030SBrian Somers       }
885e2ebb036SBrian Somers       break;
886e2ebb036SBrian Somers 
887e2ebb036SBrian Somers     default:
8889c81b87dSBrian Somers       datalink_ComeDown(dl, how);
889c7cc5030SBrian Somers   }
890e2ebb036SBrian Somers }
891c7cc5030SBrian Somers 
892c7cc5030SBrian Somers void
8939c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
894c7cc5030SBrian Somers {
895c7cc5030SBrian Somers   /* Carrier is lost */
896e2ebb036SBrian Somers   switch (dl->state) {
897e2ebb036SBrian Somers     case DATALINK_OPEN:
898643f4904SBrian Somers       peerid_Init(&dl->peer);
89909206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
900e2ebb036SBrian Somers       /* fall through */
901e2ebb036SBrian Somers 
90292b09558SBrian Somers     case DATALINK_CBCP:
903e2ebb036SBrian Somers     case DATALINK_AUTH:
904e2ebb036SBrian Somers     case DATALINK_LCP:
90509206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
906e2ebb036SBrian Somers       /* fall through */
907c7cc5030SBrian Somers 
908e2ebb036SBrian Somers     default:
9099c81b87dSBrian Somers       datalink_ComeDown(dl, how);
910c7cc5030SBrian Somers   }
911e2ebb036SBrian Somers }
912c7cc5030SBrian Somers 
913c7cc5030SBrian Somers void
9143006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9153006ec67SBrian Somers {
9163006ec67SBrian Somers   dl->reconnect_tries = 0;
9173006ec67SBrian Somers }
918c7cc5030SBrian Somers 
9199c81b87dSBrian Somers void
9209c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9219c81b87dSBrian Somers {
9227729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9239c81b87dSBrian Somers     dl->stayonline = 1;
9249c81b87dSBrian Somers }
9259c81b87dSBrian Somers 
926643f4904SBrian Somers int
927643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
928c7cc5030SBrian Somers {
929643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
930643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
931643f4904SBrian Somers                 datalink_State(arg->cx));
932643f4904SBrian Somers   prompt_Printf(arg->prompt, " CHAP Encryption:    %s\n",
933643f4904SBrian Somers                 arg->cx->chap.using_MSChap ? "MSChap" : "MD5" );
934643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
935643f4904SBrian Somers   if (*arg->cx->peer.authname)
936643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
937643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
938643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
939565e35e5SBrian Somers   else
940643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
941643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
942643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
943643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
944643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
945643f4904SBrian Somers 
946643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
947643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
948643f4904SBrian Somers                 arg->cx->cfg.phone.list);
949643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
950643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
951643f4904SBrian Somers                   arg->cx->cfg.dial.max);
952565e35e5SBrian Somers   else
953643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
954643f4904SBrian Somers   if (arg->cx->cfg.dial.next_timeout > 0)
955643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
956565e35e5SBrian Somers   else
957643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
958643f4904SBrian Somers   if (arg->cx->cfg.dial.timeout > 0)
959643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
960565e35e5SBrian Somers   else
961643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
962643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
963643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
964643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
965643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
966643f4904SBrian Somers   else
967643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
96892b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
96992b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
97092b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
97192b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
97292b09558SBrian Somers   else {
97392b09558SBrian Somers     int comma = 0;
97492b09558SBrian Somers 
97592b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
97692b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
97792b09558SBrian Somers       comma = 1;
97892b09558SBrian Somers     }
97992b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
98092b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
98192b09558SBrian Somers       comma = 1;
98292b09558SBrian Somers     }
98392b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
98492b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
98592b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
98692b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
98792b09558SBrian Somers       comma = 1;
98892b09558SBrian Somers     }
98992b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
99092b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
99192b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
99292b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
993cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
994cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
995cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
996cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
997cf784a89SBrian Somers         else
998cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
999cf784a89SBrian Somers       } else
1000cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
100192b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
100292b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
100392b09558SBrian Somers     } else
100492b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
100592b09558SBrian Somers   }
100692b09558SBrian Somers 
1007643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1008643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1009643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1010643f4904SBrian Somers                 arg->cx->cfg.script.login);
1011643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1012643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1013643f4904SBrian Somers   return 0;
1014565e35e5SBrian Somers }
1015565e35e5SBrian Somers 
1016565e35e5SBrian Somers int
1017565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1018565e35e5SBrian Somers {
101925092092SBrian Somers   if (arg->argc == arg->argn+2) {
102025092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
102125092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1022565e35e5SBrian Somers     return 0;
1023565e35e5SBrian Somers   }
1024565e35e5SBrian Somers   return -1;
1025565e35e5SBrian Somers }
1026565e35e5SBrian Somers 
1027565e35e5SBrian Somers int
1028565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1029565e35e5SBrian Somers {
1030565e35e5SBrian Somers   int timeout;
1031565e35e5SBrian Somers   int tries;
1032565e35e5SBrian Somers   char *dot;
1033565e35e5SBrian Somers 
103425092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
103525092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
103625092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1037565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1038565e35e5SBrian Somers       randinit();
1039565e35e5SBrian Somers     } else {
104025092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1041565e35e5SBrian Somers 
1042565e35e5SBrian Somers       if (timeout >= 0)
1043565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1044565e35e5SBrian Somers       else {
1045dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1046565e35e5SBrian Somers 	return -1;
1047565e35e5SBrian Somers       }
1048565e35e5SBrian Somers     }
1049565e35e5SBrian Somers 
105025092092SBrian Somers     dot = strchr(arg->argv[arg->argn], '.');
1051565e35e5SBrian Somers     if (dot) {
1052565e35e5SBrian Somers       if (strcasecmp(++dot, "random") == 0) {
1053565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1054565e35e5SBrian Somers 	randinit();
1055565e35e5SBrian Somers       } else {
1056565e35e5SBrian Somers 	timeout = atoi(dot);
1057565e35e5SBrian Somers 	if (timeout >= 0)
1058565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1059565e35e5SBrian Somers 	else {
1060dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1061565e35e5SBrian Somers 	  return -1;
1062565e35e5SBrian Somers 	}
1063565e35e5SBrian Somers       }
1064565e35e5SBrian Somers     } else
1065565e35e5SBrian Somers       /* Default next timeout */
1066565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1067565e35e5SBrian Somers 
106825092092SBrian Somers     if (arg->argc == arg->argn+2) {
106925092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1070565e35e5SBrian Somers 
1071565e35e5SBrian Somers       if (tries >= 0) {
1072565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1073565e35e5SBrian Somers       } else {
1074dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1075565e35e5SBrian Somers 	return 1;
1076565e35e5SBrian Somers       }
1077565e35e5SBrian Somers     }
1078565e35e5SBrian Somers     return 0;
1079565e35e5SBrian Somers   }
1080565e35e5SBrian Somers   return -1;
1081c7cc5030SBrian Somers }
1082c7cc5030SBrian Somers 
1083cdbbb6b5SBrian Somers static const char *states[] = {
1084565e35e5SBrian Somers   "closed",
1085565e35e5SBrian Somers   "opening",
1086565e35e5SBrian Somers   "hangup",
1087565e35e5SBrian Somers   "dial",
1088565e35e5SBrian Somers   "login",
1089565e35e5SBrian Somers   "ready",
1090565e35e5SBrian Somers   "lcp",
1091565e35e5SBrian Somers   "auth",
109292b09558SBrian Somers   "cbcp",
1093565e35e5SBrian Somers   "open"
1094c7cc5030SBrian Somers };
1095c7cc5030SBrian Somers 
1096643f4904SBrian Somers const char *
1097c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1098c7cc5030SBrian Somers {
1099c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1100c7cc5030SBrian Somers     return "unknown";
1101c7cc5030SBrian Somers   return states[dl->state];
1102c7cc5030SBrian Somers }
11036f384573SBrian Somers 
11049ae58882SBrian Somers static void
11059ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
11069ae58882SBrian Somers {
11079ae58882SBrian Somers   if (state != dl->state) {
11089ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
11099ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
11109ae58882SBrian Somers                  states[state]);
11119ae58882SBrian Somers       dl->state = state;
11129ae58882SBrian Somers     } else
11139ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
11149ae58882SBrian Somers   }
11159ae58882SBrian Somers }
11169ae58882SBrian Somers 
11176f384573SBrian Somers struct datalink *
111896c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
111996c9bb21SBrian Somers              int fd)
11206f384573SBrian Somers {
1121b7c5748eSBrian Somers   struct datalink *dl, *cdl;
11226f384573SBrian Somers   u_int retry;
1123b7c5748eSBrian Somers   char *oname;
11246f384573SBrian Somers 
112596c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
112696c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
11276f384573SBrian Somers 
112896c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
112996c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
113096c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
113196c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
11326f384573SBrian Somers   }
1133b7c5748eSBrian Somers 
1134b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1135b7c5748eSBrian Somers   oname = NULL;
1136b7c5748eSBrian Somers   do {
1137b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1138b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1139b7c5748eSBrian Somers         if (oname)
1140b7c5748eSBrian Somers           free(datalink_NextName(dl));
1141b7c5748eSBrian Somers         else
1142b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1143b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1144b7c5748eSBrian Somers       }
1145b7c5748eSBrian Somers   } while (cdl);
1146b7c5748eSBrian Somers 
1147b7c5748eSBrian Somers   if (oname) {
1148b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1149b7c5748eSBrian Somers     free(oname);
1150b7c5748eSBrian Somers   } else {
115196c9bb21SBrian Somers     dl->name = strdup(dl->name);
1152b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1153b7c5748eSBrian Somers   }
1154b7c5748eSBrian Somers   (*niov)++;
11556f384573SBrian Somers 
11566f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
11576f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
11586f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
11596f384573SBrian Somers   dl->desc.Read = datalink_Read;
11606f384573SBrian Somers   dl->desc.Write = datalink_Write;
11616f384573SBrian Somers 
11626f384573SBrian Somers   mp_linkInit(&dl->mp);
11636f384573SBrian Somers   *dl->phone.list = '\0';
11646f384573SBrian Somers   dl->phone.next = NULL;
11656f384573SBrian Somers   dl->phone.alt = NULL;
11666f384573SBrian Somers   dl->phone.chosen = "N/A";
11676f384573SBrian Somers 
11686f384573SBrian Somers   dl->bundle = bundle;
11696f384573SBrian Somers   dl->next = NULL;
11706f384573SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
11716f384573SBrian Somers   dl->dial_tries = 0;
11726f384573SBrian Somers   dl->reconnect_tries = 0;
11736f384573SBrian Somers   dl->parent = &bundle->fsm;
11746f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
11756f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
11766f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
11776f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
11786f384573SBrian Somers   dl->fsmp.object = dl;
11796f384573SBrian Somers 
11806f384573SBrian Somers   retry = dl->pap.cfg.fsmretry;
1181dd7e2610SBrian Somers   auth_Init(&dl->pap);
11826f384573SBrian Somers   dl->pap.cfg.fsmretry = retry;
11836f384573SBrian Somers 
11846f384573SBrian Somers   retry = dl->chap.auth.cfg.fsmretry;
1185dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
11866f384573SBrian Somers   dl->chap.auth.cfg.fsmretry = retry;
11876f384573SBrian Somers 
118896c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
118996c9bb21SBrian Somers 
119096c9bb21SBrian Somers   if (!dl->physical) {
11916f384573SBrian Somers     free(dl->name);
11926f384573SBrian Somers     free(dl);
11936f384573SBrian Somers     dl = NULL;
11949ae58882SBrian Somers   } else {
119592b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
11966f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
11976f384573SBrian Somers 
11989ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
11999ae58882SBrian Somers               dl->name, datalink_State(dl));
12009ae58882SBrian Somers   }
12019ae58882SBrian Somers 
12026f384573SBrian Somers   return dl;
12036f384573SBrian Somers }
12046f384573SBrian Somers 
12056f384573SBrian Somers int
120685fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
120785fd273aSBrian Somers              pid_t newpid)
12086f384573SBrian Somers {
120996c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
121096c9bb21SBrian Somers   int link_fd;
12116f384573SBrian Somers 
121296c9bb21SBrian Somers   if (dl) {
1213dd7e2610SBrian Somers     timer_Stop(&dl->dial_timer);
121492b09558SBrian Somers     /* The following is purely for the sake of paranoia */
121592b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1216dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1217dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
12186f384573SBrian Somers   }
12196f384573SBrian Somers 
122096c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
122196c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
122296c9bb21SBrian Somers     if (dl) {
12236f384573SBrian Somers       free(dl->name);
12246f384573SBrian Somers       free(dl);
122596c9bb21SBrian Somers     }
122696c9bb21SBrian Somers     return -1;
122796c9bb21SBrian Somers   }
122896c9bb21SBrian Somers 
122996c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
123096c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
123196c9bb21SBrian Somers   iov[*niov].iov_base =
123296c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
123396c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
123496c9bb21SBrian Somers 
123585fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
123696c9bb21SBrian Somers 
123796c9bb21SBrian Somers   if (link_fd == -1 && dl) {
123896c9bb21SBrian Somers     free(dl->name);
123996c9bb21SBrian Somers     free(dl);
124096c9bb21SBrian Somers   }
12416f384573SBrian Somers 
12426f384573SBrian Somers   return link_fd;
12436f384573SBrian Somers }
12446f384573SBrian Somers 
124558d55334SBrian Somers void
124658d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
124758d55334SBrian Somers {
124858d55334SBrian Somers   free(dl->name);
124958d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
125058d55334SBrian Somers }
125158d55334SBrian Somers 
125284917b87SBrian Somers char *
125384917b87SBrian Somers datalink_NextName(struct datalink *dl)
12546f384573SBrian Somers {
12556f384573SBrian Somers   int f, n;
125684917b87SBrian Somers   char *name, *oname;
12576f384573SBrian Somers 
12586f384573SBrian Somers   n = strlen(dl->name);
12596f384573SBrian Somers   name = (char *)malloc(n+3);
12606f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
12616f384573SBrian Somers     if (!isdigit(dl->name[f]))
12626f384573SBrian Somers       break;
12636f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
12646f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
126584917b87SBrian Somers   oname = dl->name;
126654cd8e13SBrian Somers   dl->name = name;
126754cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
126884917b87SBrian Somers   return oname;
12696f384573SBrian Somers }
1270dd0645c5SBrian Somers 
1271dd0645c5SBrian Somers int
1272dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1273dd0645c5SBrian Somers {
1274dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1275dd0645c5SBrian Somers     return 0;
1276dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1277dd0645c5SBrian Somers     dl->script.run = 0;
1278dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1279dd0645c5SBrian Somers     dl->reconnect_tries = 0;
128081358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1281dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1282dd0645c5SBrian Somers   return 1;
1283dd0645c5SBrian Somers }
1284