xref: /freebsd/usr.sbin/ppp/datalink.c (revision cf784a89)
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  *
26cf784a89SBrian Somers  *	$Id: datalink.c,v 1.19 1998/08/18 00:53:48 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);
2591342caedSBrian Somers             return datalink_UpdateSet(d, r, w, e, n);
260c5a5a6caSBrian Somers           } else
261c5a5a6caSBrian Somers             datalink_LoginDone(dl);
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:
306c5a5a6caSBrian Somers               datalink_LoginDone(dl);
3073006ec67SBrian Somers               break;
3083006ec67SBrian Somers           }
3093006ec67SBrian Somers           break;
3103006ec67SBrian Somers         case CHAT_FAILED:
3113006ec67SBrian Somers           /* Going down - script failed */
312dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
31339d94652SBrian Somers           chat_Destroy(&dl->chat);
3143006ec67SBrian Somers           switch(dl->state) {
3153006ec67SBrian Somers             case DATALINK_HANGUP:
3163006ec67SBrian Somers               datalink_HangupDone(dl);
3173006ec67SBrian Somers               break;
3183006ec67SBrian Somers             case DATALINK_DIAL:
3193006ec67SBrian Somers             case DATALINK_LOGIN:
3209ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
321d4af231cSBrian Somers               modem_Offline(dl->physical);	/* Is this required ? */
322f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
3231342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3243006ec67SBrian Somers           }
3253006ec67SBrian Somers           break;
3263006ec67SBrian Somers       }
3273006ec67SBrian Somers       break;
328c7cc5030SBrian Somers 
329c7cc5030SBrian Somers     case DATALINK_READY:
330e2ebb036SBrian Somers     case DATALINK_LCP:
331e2ebb036SBrian Somers     case DATALINK_AUTH:
33292b09558SBrian Somers     case DATALINK_CBCP:
3333006ec67SBrian Somers     case DATALINK_OPEN:
3343006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3353006ec67SBrian Somers       break;
3363006ec67SBrian Somers   }
3373006ec67SBrian Somers   return result;
3383006ec67SBrian Somers }
3393006ec67SBrian Somers 
340ea722969SBrian Somers int
341ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
342ea722969SBrian Somers {
343ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
344ea722969SBrian Somers }
345ea722969SBrian Somers 
3463006ec67SBrian Somers static int
3472f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3483006ec67SBrian Somers {
3493006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3503006ec67SBrian Somers 
3513006ec67SBrian Somers   switch (dl->state) {
3523006ec67SBrian Somers     case DATALINK_CLOSED:
353c7cc5030SBrian Somers     case DATALINK_OPENING:
3543006ec67SBrian Somers       break;
355c7cc5030SBrian Somers 
3563006ec67SBrian Somers     case DATALINK_HANGUP:
3573006ec67SBrian Somers     case DATALINK_DIAL:
3583006ec67SBrian Somers     case DATALINK_LOGIN:
3593006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
360c7cc5030SBrian Somers 
361c7cc5030SBrian Somers     case DATALINK_READY:
362e2ebb036SBrian Somers     case DATALINK_LCP:
363e2ebb036SBrian Somers     case DATALINK_AUTH:
36492b09558SBrian Somers     case DATALINK_CBCP:
3653006ec67SBrian Somers     case DATALINK_OPEN:
3663006ec67SBrian Somers       return descriptor_IsSet(&dl->physical->desc, fdset);
3673006ec67SBrian Somers   }
3683006ec67SBrian Somers   return 0;
3693006ec67SBrian Somers }
3703006ec67SBrian Somers 
3713006ec67SBrian Somers static void
3723006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3733006ec67SBrian Somers {
3743006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3753006ec67SBrian Somers 
3763006ec67SBrian Somers   switch (dl->state) {
3773006ec67SBrian Somers     case DATALINK_CLOSED:
378c7cc5030SBrian Somers     case DATALINK_OPENING:
3793006ec67SBrian Somers       break;
380c7cc5030SBrian Somers 
3813006ec67SBrian Somers     case DATALINK_HANGUP:
3823006ec67SBrian Somers     case DATALINK_DIAL:
3833006ec67SBrian Somers     case DATALINK_LOGIN:
3843006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
3853006ec67SBrian Somers       break;
386c7cc5030SBrian Somers 
387c7cc5030SBrian Somers     case DATALINK_READY:
388e2ebb036SBrian Somers     case DATALINK_LCP:
389e2ebb036SBrian Somers     case DATALINK_AUTH:
39092b09558SBrian Somers     case DATALINK_CBCP:
3913006ec67SBrian Somers     case DATALINK_OPEN:
3923006ec67SBrian Somers       descriptor_Read(&dl->physical->desc, bundle, fdset);
3933006ec67SBrian Somers       break;
3943006ec67SBrian Somers   }
3953006ec67SBrian Somers }
3963006ec67SBrian Somers 
3971af29a6eSBrian Somers static int
398f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3993006ec67SBrian Somers {
4003006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4011af29a6eSBrian Somers   int result = 0;
4023006ec67SBrian Somers 
4033006ec67SBrian Somers   switch (dl->state) {
4043006ec67SBrian Somers     case DATALINK_CLOSED:
405c7cc5030SBrian Somers     case DATALINK_OPENING:
4063006ec67SBrian Somers       break;
407c7cc5030SBrian Somers 
4083006ec67SBrian Somers     case DATALINK_HANGUP:
4093006ec67SBrian Somers     case DATALINK_DIAL:
4103006ec67SBrian Somers     case DATALINK_LOGIN:
4111af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4123006ec67SBrian Somers       break;
413c7cc5030SBrian Somers 
414c7cc5030SBrian Somers     case DATALINK_READY:
415e2ebb036SBrian Somers     case DATALINK_LCP:
416e2ebb036SBrian Somers     case DATALINK_AUTH:
41792b09558SBrian Somers     case DATALINK_CBCP:
4183006ec67SBrian Somers     case DATALINK_OPEN:
4191af29a6eSBrian Somers       result = descriptor_Write(&dl->physical->desc, bundle, fdset);
4203006ec67SBrian Somers       break;
4213006ec67SBrian Somers   }
4221af29a6eSBrian Somers 
4231af29a6eSBrian Somers   return result;
4243006ec67SBrian Somers }
4253006ec67SBrian Somers 
4266d666775SBrian Somers static void
4279c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
428e2ebb036SBrian Somers {
4299c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
430e2ebb036SBrian Somers     dl->dial_tries = -1;
431e2ebb036SBrian Somers     dl->reconnect_tries = 0;
4327729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4339c81b87dSBrian Somers       dl->stayonline = 1;
434e2ebb036SBrian Somers   }
435e2ebb036SBrian Somers 
4367729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4379c81b87dSBrian Somers     dl->stayonline = 0;
438d4af231cSBrian Somers     timer_Stop(&dl->physical->Timer);
4399c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4409c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
441e2ebb036SBrian Somers     modem_Offline(dl->physical);
442b6f5f442SBrian Somers     chat_Destroy(&dl->chat);
443e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4449ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
445f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
446e2ebb036SBrian Somers     } else
447e2ebb036SBrian Somers       datalink_HangupDone(dl);
448e2ebb036SBrian Somers   }
449e2ebb036SBrian Somers }
450e2ebb036SBrian Somers 
451e2ebb036SBrian Somers static void
4526d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4536d666775SBrian Somers {
4546d666775SBrian Somers   /* The given FSM is about to start up ! */
4556d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
456a611cad6SBrian Somers 
45792e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
458e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
459e2ebb036SBrian Somers }
4606d666775SBrian Somers 
4616d666775SBrian Somers static void
4626d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4636d666775SBrian Somers {
4646d666775SBrian Somers   /* The given fsm is now up */
4656d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4666d666775SBrian Somers 
46792e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
468643f4904SBrian Somers     datalink_GotAuthname(dl, "", 0);
4693b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth;
4703b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth;
4713b0f8d2eSBrian Somers     if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) {
4723b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH)
4735563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
474dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
4753b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.his_auth),
4763b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.want_auth));
4773b0f8d2eSBrian Somers       if (dl->physical->link.lcp.his_auth == PROTO_PAP)
478dd7e2610SBrian Somers         auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge);
4793b0f8d2eSBrian Somers       if (dl->physical->link.lcp.want_auth == PROTO_CHAP)
480dd7e2610SBrian Somers         auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge);
481e2ebb036SBrian Somers     } else
482e2ebb036SBrian Somers       datalink_AuthOk(dl);
483e2ebb036SBrian Somers   }
484e2ebb036SBrian Somers }
485e2ebb036SBrian Somers 
486e2ebb036SBrian Somers void
487643f4904SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name, int len)
488643f4904SBrian Somers {
489643f4904SBrian Somers   if (len >= sizeof dl->peer.authname)
490643f4904SBrian Somers     len = sizeof dl->peer.authname - 1;
491643f4904SBrian Somers   strncpy(dl->peer.authname, name, len);
492643f4904SBrian Somers   dl->peer.authname[len] = '\0';
493643f4904SBrian Somers }
494643f4904SBrian Somers 
495643f4904SBrian Somers void
49692b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
497e2ebb036SBrian Somers {
49806337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
4991df0a3b9SBrian Somers 
500dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5011fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5021fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5031fa665f5SBrian Somers       case MP_LINKSENT:
504ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5051fa665f5SBrian Somers         return;
5061fa665f5SBrian Somers       case MP_UP:
5070a1b5c9dSBrian Somers         /* First link in the bundle */
50892b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
5090a1b5c9dSBrian Somers         /* fall through */
5101fa665f5SBrian Somers       case MP_ADDED:
5110a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5121df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
5131fa665f5SBrian Somers         break;
5141fa665f5SBrian Somers       case MP_FAILED:
51549052c95SBrian Somers         datalink_AuthNotOk(dl);
51649052c95SBrian Somers         return;
51749052c95SBrian Somers     }
51849052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
519dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
520897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
521897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
52249052c95SBrian Somers     return;
52350abd4c8SBrian Somers   } else {
52450abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
525ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
52692b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
52750abd4c8SBrian Somers   }
52849052c95SBrian Somers 
52906337856SBrian Somers   if (ccpok) {
530dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
531dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
53206337856SBrian Somers   }
5339ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5343b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5353b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5366d666775SBrian Somers }
537e2ebb036SBrian Somers 
538e2ebb036SBrian Somers void
53992b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
54092b09558SBrian Somers {
54192b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
54292b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
54392b09558SBrian Somers }
54492b09558SBrian Somers 
54592b09558SBrian Somers void
54692b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
54792b09558SBrian Somers {
54892b09558SBrian Somers   cbcp_Down(&dl->cbcp);
54992b09558SBrian Somers   datalink_CBCPComplete(dl);
55092b09558SBrian Somers }
55192b09558SBrian Somers 
55292b09558SBrian Somers void
55392b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
55492b09558SBrian Somers {
55592b09558SBrian Somers   if (dl->physical->link.lcp.his_callback.opmask ==
55692b09558SBrian Somers       CALLBACK_BIT(CALLBACK_CBCP) ||
55792b09558SBrian Somers       dl->physical->link.lcp.want_callback.opmask ==
55892b09558SBrian Somers       CALLBACK_BIT(CALLBACK_CBCP)) {
55992b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
56092b09558SBrian Somers     cbcp_Up(&dl->cbcp);
56192b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
56292b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
56392b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
56492b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
56592b09558SBrian Somers   } else
56692b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
56792b09558SBrian Somers       case 0:
56892b09558SBrian Somers         datalink_NCPUp(dl);
56992b09558SBrian Somers         break;
57092b09558SBrian Somers 
57192b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
57292b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
57392b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
57492b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
57592b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
57692b09558SBrian Somers                      dl->peer.authname);
57792b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
57892b09558SBrian Somers         } else {
57992b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
58092b09558SBrian Somers           if (ptr)
58192b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
58292b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
58392b09558SBrian Somers                      dl->cbcp.fsm.phone);
58492b09558SBrian Somers           dl->cbcp.required = 1;
58592b09558SBrian Somers         }
58692b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
58792b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
58892b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
58992b09558SBrian Somers         break;
59092b09558SBrian Somers 
59192b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
59292b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
59392b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
59492b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
59592b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
59692b09558SBrian Somers                    dl->cbcp.fsm.phone);
59792b09558SBrian Somers         dl->cbcp.required = 1;
59892b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
59992b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
60092b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
60192b09558SBrian Somers         break;
60292b09558SBrian Somers 
60392b09558SBrian Somers       default:
60492b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
60592b09558SBrian Somers                    dl->name);
60692b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
60792b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
60892b09558SBrian Somers         break;
60992b09558SBrian Somers     }
61092b09558SBrian Somers }
61192b09558SBrian Somers 
61292b09558SBrian Somers void
613e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
614e2ebb036SBrian Somers {
6159ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
616dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6176d666775SBrian Somers }
6186d666775SBrian Somers 
6196d666775SBrian Somers static void
6206d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6216d666775SBrian Somers {
6226d666775SBrian Somers   /* The given FSM has been told to come down */
6236d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
624a611cad6SBrian Somers 
62592e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
626e2ebb036SBrian Somers     switch (dl->state) {
627e2ebb036SBrian Somers       case DATALINK_OPEN:
628643f4904SBrian Somers         peerid_Init(&dl->peer);
62909206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
630ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
631e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
63292b09558SBrian Somers         /* fall through (just in case) */
63392b09558SBrian Somers 
63492b09558SBrian Somers       case DATALINK_CBCP:
63592b09558SBrian Somers         if (!dl->cbcp.required)
63692b09558SBrian Somers           cbcp_Down(&dl->cbcp);
63792b09558SBrian Somers         /* fall through (just in case) */
638e2ebb036SBrian Somers 
639e2ebb036SBrian Somers       case DATALINK_AUTH:
640dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
641dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
6426d666775SBrian Somers     }
6439ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
644e2ebb036SBrian Somers   }
6456d666775SBrian Somers }
6466d666775SBrian Somers 
6476d666775SBrian Somers static void
6486d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
6496d666775SBrian Somers {
6506d666775SBrian Somers   /* The given fsm is now down */
6516d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
6526d666775SBrian Somers 
65392e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
65409206a6fSBrian Somers     fsm2initial(fp);
6556d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
6569c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
6570a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
6580a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
6596d666775SBrian Somers }
6606d666775SBrian Somers 
6613006ec67SBrian Somers struct datalink *
6626f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
6633006ec67SBrian Somers {
6643006ec67SBrian Somers   struct datalink *dl;
6653006ec67SBrian Somers 
6663006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
6673006ec67SBrian Somers   if (dl == NULL)
6683006ec67SBrian Somers     return dl;
6693006ec67SBrian Somers 
6703006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
6713006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
6723006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
6733006ec67SBrian Somers   dl->desc.Read = datalink_Read;
6743006ec67SBrian Somers   dl->desc.Write = datalink_Write;
6755b8b8060SBrian Somers 
676e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
677e718d1d7SBrian Somers 
67873a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
67973a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
68073a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
681f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
682f9545805SBrian Somers   *dl->phone.list = '\0';
683f9545805SBrian Somers   dl->phone.next = NULL;
684f9545805SBrian Somers   dl->phone.alt = NULL;
685f9545805SBrian Somers   dl->phone.chosen = "N/A";
6869c81b87dSBrian Somers   dl->stayonline = 0;
687c7cc5030SBrian Somers   dl->script.run = 1;
688c7cc5030SBrian Somers   dl->script.packetmode = 1;
6893b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
6905b8b8060SBrian Somers 
6913006ec67SBrian Somers   dl->bundle = bundle;
6923006ec67SBrian Somers   dl->next = NULL;
693e718d1d7SBrian Somers 
6943006ec67SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
695e718d1d7SBrian Somers 
6963006ec67SBrian Somers   dl->dial_tries = 0;
697565e35e5SBrian Somers   dl->cfg.dial.max = 1;
698565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
699565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
700e718d1d7SBrian Somers 
701abff9baeSBrian Somers   dl->reconnect_tries = 0;
702565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
703565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
704abff9baeSBrian Somers 
70592b09558SBrian Somers   dl->cfg.callback.opmask = 0;
70692b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
70792b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
70892b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
70992b09558SBrian Somers 
7103006ec67SBrian Somers   dl->name = strdup(name);
711643f4904SBrian Somers   peerid_Init(&dl->peer);
7126f384573SBrian Somers   dl->parent = &bundle->fsm;
7133b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7143b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7153b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7163b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7173b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7183b0f8d2eSBrian Somers 
719dd7e2610SBrian Somers   auth_Init(&dl->pap);
720dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
7213b0f8d2eSBrian Somers 
722565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
7233006ec67SBrian Somers     free(dl->name);
7243006ec67SBrian Somers     free(dl);
7253006ec67SBrian Somers     return NULL;
7263006ec67SBrian Somers   }
72792b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
728f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
7293006ec67SBrian Somers 
7309ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
7319ae58882SBrian Somers              dl->name, datalink_State(dl));
7323006ec67SBrian Somers 
7333006ec67SBrian Somers   return dl;
7343006ec67SBrian Somers }
7353006ec67SBrian Somers 
7363006ec67SBrian Somers struct datalink *
737cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
738cd7bd93aSBrian Somers {
739cd7bd93aSBrian Somers   struct datalink *dl;
740cd7bd93aSBrian Somers 
741cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
742cd7bd93aSBrian Somers   if (dl == NULL)
743cd7bd93aSBrian Somers     return dl;
744cd7bd93aSBrian Somers 
745cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
746cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
747cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
748cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
749cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
750cd7bd93aSBrian Somers 
751cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
752cd7bd93aSBrian Somers 
753cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
754cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
755cd7bd93aSBrian Somers   *dl->phone.list = '\0';
756643f4904SBrian Somers   dl->phone.next = NULL;
757643f4904SBrian Somers   dl->phone.alt = NULL;
758643f4904SBrian Somers   dl->phone.chosen = "N/A";
759cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
760cd7bd93aSBrian Somers   dl->next = NULL;
761cd7bd93aSBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
762cd7bd93aSBrian Somers   dl->dial_tries = 0;
763cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
764cd7bd93aSBrian Somers   dl->name = strdup(name);
765643f4904SBrian Somers   peerid_Init(&dl->peer);
766cd7bd93aSBrian Somers   dl->parent = odl->parent;
767cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
768643f4904SBrian Somers   dl->fsmp.object = dl;
769dd7e2610SBrian Somers   auth_Init(&dl->pap);
770cd7bd93aSBrian Somers   dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry;
771cd7bd93aSBrian Somers 
772dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
773cd7bd93aSBrian Somers   dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry;
774cd7bd93aSBrian Somers 
77581358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
776cd7bd93aSBrian Somers     free(dl->name);
777cd7bd93aSBrian Somers     free(dl);
778cd7bd93aSBrian Somers     return NULL;
779cd7bd93aSBrian Somers   }
780cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
781cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
782cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
783cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
784cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
785cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
786cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
787cd7bd93aSBrian Somers 
78892b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
789cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
790cd7bd93aSBrian Somers 
7919ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
7929ae58882SBrian Somers              dl->name, datalink_State(dl));
793cd7bd93aSBrian Somers 
794cd7bd93aSBrian Somers   return dl;
795cd7bd93aSBrian Somers }
796cd7bd93aSBrian Somers 
797cd7bd93aSBrian Somers struct datalink *
7983006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
7993006ec67SBrian Somers {
8003006ec67SBrian Somers   struct datalink *result;
8013006ec67SBrian Somers 
80239d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
803dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
804c7cc5030SBrian Somers               datalink_State(dl));
80539d94652SBrian Somers     switch (dl->state) {
80639d94652SBrian Somers       case DATALINK_HANGUP:
80739d94652SBrian Somers       case DATALINK_DIAL:
80839d94652SBrian Somers       case DATALINK_LOGIN:
80939d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
81039d94652SBrian Somers         break;
81139d94652SBrian Somers     }
81239d94652SBrian Somers   }
8133006ec67SBrian Somers 
8143006ec67SBrian Somers   result = dl->next;
8153b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
8163006ec67SBrian Somers   free(dl->name);
8173006ec67SBrian Somers   free(dl);
8183006ec67SBrian Somers 
8193006ec67SBrian Somers   return result;
8203006ec67SBrian Somers }
8213006ec67SBrian Somers 
8223006ec67SBrian Somers void
823c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8243006ec67SBrian Somers {
8256f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
826565e35e5SBrian Somers     /* Ignore scripts */
827565e35e5SBrian Somers     runscripts = 0;
828565e35e5SBrian Somers 
829c7cc5030SBrian Somers   switch (dl->state) {
830c7cc5030SBrian Somers     case DATALINK_CLOSED:
8313b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
8323b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
8333b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
8349ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
835565e35e5SBrian Somers       dl->reconnect_tries =
8366f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
837565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
838c5a5a6caSBrian Somers       dl->script.run = runscripts;
839c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
840c7cc5030SBrian Somers       break;
841c7cc5030SBrian Somers 
842c7cc5030SBrian Somers     case DATALINK_OPENING:
843c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
844c7cc5030SBrian Somers         dl->script.run = 1;
845c7cc5030SBrian Somers       /* fall through */
846c7cc5030SBrian Somers 
847c7cc5030SBrian Somers     case DATALINK_DIAL:
848c7cc5030SBrian Somers     case DATALINK_LOGIN:
849c7cc5030SBrian Somers     case DATALINK_READY:
850c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
851c7cc5030SBrian Somers         dl->script.packetmode = 1;
852c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
853c7cc5030SBrian Somers           datalink_LoginDone(dl);
854c7cc5030SBrian Somers       }
855c7cc5030SBrian Somers       break;
8563006ec67SBrian Somers   }
8573006ec67SBrian Somers }
8583006ec67SBrian Somers 
8593006ec67SBrian Somers void
8609c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
861c7cc5030SBrian Somers {
862c7cc5030SBrian Somers   /* Please close */
863e2ebb036SBrian Somers   switch (dl->state) {
864e2ebb036SBrian Somers     case DATALINK_OPEN:
865643f4904SBrian Somers       peerid_Init(&dl->peer);
86609206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
867e2ebb036SBrian Somers       /* fall through */
868e2ebb036SBrian Somers 
86992b09558SBrian Somers     case DATALINK_CBCP:
870e2ebb036SBrian Somers     case DATALINK_AUTH:
871e2ebb036SBrian Somers     case DATALINK_LCP:
872dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
8739c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
874c7cc5030SBrian Somers         dl->dial_tries = -1;
875c7cc5030SBrian Somers         dl->reconnect_tries = 0;
8769c81b87dSBrian Somers         if (how == CLOSE_LCP)
8779c81b87dSBrian Somers           dl->stayonline = 1;
878c7cc5030SBrian Somers       }
879e2ebb036SBrian Somers       break;
880e2ebb036SBrian Somers 
881e2ebb036SBrian Somers     default:
8829c81b87dSBrian Somers       datalink_ComeDown(dl, how);
883c7cc5030SBrian Somers   }
884e2ebb036SBrian Somers }
885c7cc5030SBrian Somers 
886c7cc5030SBrian Somers void
8879c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
888c7cc5030SBrian Somers {
889c7cc5030SBrian Somers   /* Carrier is lost */
890e2ebb036SBrian Somers   switch (dl->state) {
891e2ebb036SBrian Somers     case DATALINK_OPEN:
892643f4904SBrian Somers       peerid_Init(&dl->peer);
89309206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
894e2ebb036SBrian Somers       /* fall through */
895e2ebb036SBrian Somers 
89692b09558SBrian Somers     case DATALINK_CBCP:
897e2ebb036SBrian Somers     case DATALINK_AUTH:
898e2ebb036SBrian Somers     case DATALINK_LCP:
89909206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
900e2ebb036SBrian Somers       /* fall through */
901c7cc5030SBrian Somers 
902e2ebb036SBrian Somers     default:
9039c81b87dSBrian Somers       datalink_ComeDown(dl, how);
904c7cc5030SBrian Somers   }
905e2ebb036SBrian Somers }
906c7cc5030SBrian Somers 
907c7cc5030SBrian Somers void
9083006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9093006ec67SBrian Somers {
9103006ec67SBrian Somers   dl->reconnect_tries = 0;
9113006ec67SBrian Somers }
912c7cc5030SBrian Somers 
9139c81b87dSBrian Somers void
9149c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9159c81b87dSBrian Somers {
9167729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9179c81b87dSBrian Somers     dl->stayonline = 1;
9189c81b87dSBrian Somers }
9199c81b87dSBrian Somers 
920643f4904SBrian Somers int
921643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
922c7cc5030SBrian Somers {
923643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
924643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
925643f4904SBrian Somers                 datalink_State(arg->cx));
926643f4904SBrian Somers   prompt_Printf(arg->prompt, " CHAP Encryption:    %s\n",
927643f4904SBrian Somers                 arg->cx->chap.using_MSChap ? "MSChap" : "MD5" );
928643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
929643f4904SBrian Somers   if (*arg->cx->peer.authname)
930643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
931643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
932643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
933565e35e5SBrian Somers   else
934643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
935643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
936643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
937643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
938643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
939643f4904SBrian Somers 
940643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
941643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
942643f4904SBrian Somers                 arg->cx->cfg.phone.list);
943643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
944643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
945643f4904SBrian Somers                   arg->cx->cfg.dial.max);
946565e35e5SBrian Somers   else
947643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
948643f4904SBrian Somers   if (arg->cx->cfg.dial.next_timeout > 0)
949643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
950565e35e5SBrian Somers   else
951643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
952643f4904SBrian Somers   if (arg->cx->cfg.dial.timeout > 0)
953643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
954565e35e5SBrian Somers   else
955643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
956643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
957643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
958643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
959643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
960643f4904SBrian Somers   else
961643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
96292b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
96392b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
96492b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
96592b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
96692b09558SBrian Somers   else {
96792b09558SBrian Somers     int comma = 0;
96892b09558SBrian Somers 
96992b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
97092b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
97192b09558SBrian Somers       comma = 1;
97292b09558SBrian Somers     }
97392b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
97492b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
97592b09558SBrian Somers       comma = 1;
97692b09558SBrian Somers     }
97792b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
97892b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
97992b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
98092b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
98192b09558SBrian Somers       comma = 1;
98292b09558SBrian Somers     }
98392b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
98492b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
98592b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
98692b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
987cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
988cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
989cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
990cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
991cf784a89SBrian Somers         else
992cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
993cf784a89SBrian Somers       } else
994cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
99592b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
99692b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
99792b09558SBrian Somers     } else
99892b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
99992b09558SBrian Somers   }
100092b09558SBrian Somers 
1001643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1002643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1003643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1004643f4904SBrian Somers                 arg->cx->cfg.script.login);
1005643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1006643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1007643f4904SBrian Somers   return 0;
1008565e35e5SBrian Somers }
1009565e35e5SBrian Somers 
1010565e35e5SBrian Somers int
1011565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1012565e35e5SBrian Somers {
101325092092SBrian Somers   if (arg->argc == arg->argn+2) {
101425092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
101525092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1016565e35e5SBrian Somers     return 0;
1017565e35e5SBrian Somers   }
1018565e35e5SBrian Somers   return -1;
1019565e35e5SBrian Somers }
1020565e35e5SBrian Somers 
1021565e35e5SBrian Somers int
1022565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1023565e35e5SBrian Somers {
1024565e35e5SBrian Somers   int timeout;
1025565e35e5SBrian Somers   int tries;
1026565e35e5SBrian Somers   char *dot;
1027565e35e5SBrian Somers 
102825092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
102925092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
103025092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1031565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1032565e35e5SBrian Somers       randinit();
1033565e35e5SBrian Somers     } else {
103425092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1035565e35e5SBrian Somers 
1036565e35e5SBrian Somers       if (timeout >= 0)
1037565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1038565e35e5SBrian Somers       else {
1039dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1040565e35e5SBrian Somers 	return -1;
1041565e35e5SBrian Somers       }
1042565e35e5SBrian Somers     }
1043565e35e5SBrian Somers 
104425092092SBrian Somers     dot = strchr(arg->argv[arg->argn], '.');
1045565e35e5SBrian Somers     if (dot) {
1046565e35e5SBrian Somers       if (strcasecmp(++dot, "random") == 0) {
1047565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1048565e35e5SBrian Somers 	randinit();
1049565e35e5SBrian Somers       } else {
1050565e35e5SBrian Somers 	timeout = atoi(dot);
1051565e35e5SBrian Somers 	if (timeout >= 0)
1052565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1053565e35e5SBrian Somers 	else {
1054dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1055565e35e5SBrian Somers 	  return -1;
1056565e35e5SBrian Somers 	}
1057565e35e5SBrian Somers       }
1058565e35e5SBrian Somers     } else
1059565e35e5SBrian Somers       /* Default next timeout */
1060565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1061565e35e5SBrian Somers 
106225092092SBrian Somers     if (arg->argc == arg->argn+2) {
106325092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1064565e35e5SBrian Somers 
1065565e35e5SBrian Somers       if (tries >= 0) {
1066565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1067565e35e5SBrian Somers       } else {
1068dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1069565e35e5SBrian Somers 	return 1;
1070565e35e5SBrian Somers       }
1071565e35e5SBrian Somers     }
1072565e35e5SBrian Somers     return 0;
1073565e35e5SBrian Somers   }
1074565e35e5SBrian Somers   return -1;
1075c7cc5030SBrian Somers }
1076c7cc5030SBrian Somers 
1077cdbbb6b5SBrian Somers static const char *states[] = {
1078565e35e5SBrian Somers   "closed",
1079565e35e5SBrian Somers   "opening",
1080565e35e5SBrian Somers   "hangup",
1081565e35e5SBrian Somers   "dial",
1082565e35e5SBrian Somers   "login",
1083565e35e5SBrian Somers   "ready",
1084565e35e5SBrian Somers   "lcp",
1085565e35e5SBrian Somers   "auth",
108692b09558SBrian Somers   "cbcp",
1087565e35e5SBrian Somers   "open"
1088c7cc5030SBrian Somers };
1089c7cc5030SBrian Somers 
1090643f4904SBrian Somers const char *
1091c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1092c7cc5030SBrian Somers {
1093c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1094c7cc5030SBrian Somers     return "unknown";
1095c7cc5030SBrian Somers   return states[dl->state];
1096c7cc5030SBrian Somers }
10976f384573SBrian Somers 
10989ae58882SBrian Somers static void
10999ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
11009ae58882SBrian Somers {
11019ae58882SBrian Somers   if (state != dl->state) {
11029ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
11039ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
11049ae58882SBrian Somers                  states[state]);
11059ae58882SBrian Somers       dl->state = state;
11069ae58882SBrian Somers     } else
11079ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
11089ae58882SBrian Somers   }
11099ae58882SBrian Somers }
11109ae58882SBrian Somers 
11116f384573SBrian Somers struct datalink *
111296c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
111396c9bb21SBrian Somers              int fd)
11146f384573SBrian Somers {
1115b7c5748eSBrian Somers   struct datalink *dl, *cdl;
11166f384573SBrian Somers   u_int retry;
1117b7c5748eSBrian Somers   char *oname;
11186f384573SBrian Somers 
111996c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
112096c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
11216f384573SBrian Somers 
112296c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
112396c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
112496c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
112596c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
11266f384573SBrian Somers   }
1127b7c5748eSBrian Somers 
1128b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1129b7c5748eSBrian Somers   oname = NULL;
1130b7c5748eSBrian Somers   do {
1131b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1132b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1133b7c5748eSBrian Somers         if (oname)
1134b7c5748eSBrian Somers           free(datalink_NextName(dl));
1135b7c5748eSBrian Somers         else
1136b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1137b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1138b7c5748eSBrian Somers       }
1139b7c5748eSBrian Somers   } while (cdl);
1140b7c5748eSBrian Somers 
1141b7c5748eSBrian Somers   if (oname) {
1142b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1143b7c5748eSBrian Somers     free(oname);
1144b7c5748eSBrian Somers   } else {
114596c9bb21SBrian Somers     dl->name = strdup(dl->name);
1146b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1147b7c5748eSBrian Somers   }
1148b7c5748eSBrian Somers   (*niov)++;
11496f384573SBrian Somers 
11506f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
11516f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
11526f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
11536f384573SBrian Somers   dl->desc.Read = datalink_Read;
11546f384573SBrian Somers   dl->desc.Write = datalink_Write;
11556f384573SBrian Somers 
11566f384573SBrian Somers   mp_linkInit(&dl->mp);
11576f384573SBrian Somers   *dl->phone.list = '\0';
11586f384573SBrian Somers   dl->phone.next = NULL;
11596f384573SBrian Somers   dl->phone.alt = NULL;
11606f384573SBrian Somers   dl->phone.chosen = "N/A";
11616f384573SBrian Somers 
11626f384573SBrian Somers   dl->bundle = bundle;
11636f384573SBrian Somers   dl->next = NULL;
11646f384573SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
11656f384573SBrian Somers   dl->dial_tries = 0;
11666f384573SBrian Somers   dl->reconnect_tries = 0;
11676f384573SBrian Somers   dl->parent = &bundle->fsm;
11686f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
11696f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
11706f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
11716f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
11726f384573SBrian Somers   dl->fsmp.object = dl;
11736f384573SBrian Somers 
11746f384573SBrian Somers   retry = dl->pap.cfg.fsmretry;
1175dd7e2610SBrian Somers   auth_Init(&dl->pap);
11766f384573SBrian Somers   dl->pap.cfg.fsmretry = retry;
11776f384573SBrian Somers 
11786f384573SBrian Somers   retry = dl->chap.auth.cfg.fsmretry;
1179dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
11806f384573SBrian Somers   dl->chap.auth.cfg.fsmretry = retry;
11816f384573SBrian Somers 
118296c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
118396c9bb21SBrian Somers 
118496c9bb21SBrian Somers   if (!dl->physical) {
11856f384573SBrian Somers     free(dl->name);
11866f384573SBrian Somers     free(dl);
11876f384573SBrian Somers     dl = NULL;
11889ae58882SBrian Somers   } else {
118992b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
11906f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
11916f384573SBrian Somers 
11929ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
11939ae58882SBrian Somers               dl->name, datalink_State(dl));
11949ae58882SBrian Somers   }
11959ae58882SBrian Somers 
11966f384573SBrian Somers   return dl;
11976f384573SBrian Somers }
11986f384573SBrian Somers 
11996f384573SBrian Somers int
120085fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
120185fd273aSBrian Somers              pid_t newpid)
12026f384573SBrian Somers {
120396c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
120496c9bb21SBrian Somers   int link_fd;
12056f384573SBrian Somers 
120696c9bb21SBrian Somers   if (dl) {
1207dd7e2610SBrian Somers     timer_Stop(&dl->dial_timer);
120892b09558SBrian Somers     /* The following is purely for the sake of paranoia */
120992b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1210dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1211dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
12126f384573SBrian Somers   }
12136f384573SBrian Somers 
121496c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
121596c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
121696c9bb21SBrian Somers     if (dl) {
12176f384573SBrian Somers       free(dl->name);
12186f384573SBrian Somers       free(dl);
121996c9bb21SBrian Somers     }
122096c9bb21SBrian Somers     return -1;
122196c9bb21SBrian Somers   }
122296c9bb21SBrian Somers 
122396c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
122496c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
122596c9bb21SBrian Somers   iov[*niov].iov_base =
122696c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
122796c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
122896c9bb21SBrian Somers 
122985fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
123096c9bb21SBrian Somers 
123196c9bb21SBrian Somers   if (link_fd == -1 && dl) {
123296c9bb21SBrian Somers     free(dl->name);
123396c9bb21SBrian Somers     free(dl);
123496c9bb21SBrian Somers   }
12356f384573SBrian Somers 
12366f384573SBrian Somers   return link_fd;
12376f384573SBrian Somers }
12386f384573SBrian Somers 
123958d55334SBrian Somers void
124058d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
124158d55334SBrian Somers {
124258d55334SBrian Somers   free(dl->name);
124358d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
124458d55334SBrian Somers }
124558d55334SBrian Somers 
124684917b87SBrian Somers char *
124784917b87SBrian Somers datalink_NextName(struct datalink *dl)
12486f384573SBrian Somers {
12496f384573SBrian Somers   int f, n;
125084917b87SBrian Somers   char *name, *oname;
12516f384573SBrian Somers 
12526f384573SBrian Somers   n = strlen(dl->name);
12536f384573SBrian Somers   name = (char *)malloc(n+3);
12546f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
12556f384573SBrian Somers     if (!isdigit(dl->name[f]))
12566f384573SBrian Somers       break;
12576f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
12586f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
125984917b87SBrian Somers   oname = dl->name;
126054cd8e13SBrian Somers   dl->name = name;
126154cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
126284917b87SBrian Somers   return oname;
12636f384573SBrian Somers }
1264dd0645c5SBrian Somers 
1265dd0645c5SBrian Somers int
1266dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1267dd0645c5SBrian Somers {
1268dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1269dd0645c5SBrian Somers     return 0;
1270dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1271dd0645c5SBrian Somers     dl->script.run = 0;
1272dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1273dd0645c5SBrian Somers     dl->reconnect_tries = 0;
127481358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1275dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1276dd0645c5SBrian Somers   return 1;
1277dd0645c5SBrian Somers }
1278