xref: /freebsd/usr.sbin/ppp/datalink.c (revision bf1d3ff6)
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  *
26bf1d3ff6SBrian Somers  *	$Id: datalink.c,v 1.17 1998/08/07 18:42: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);
442e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4439ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
444f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
445e2ebb036SBrian Somers     } else
446e2ebb036SBrian Somers       datalink_HangupDone(dl);
447e2ebb036SBrian Somers   }
448e2ebb036SBrian Somers }
449e2ebb036SBrian Somers 
450e2ebb036SBrian Somers static void
4516d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4526d666775SBrian Somers {
4536d666775SBrian Somers   /* The given FSM is about to start up ! */
4546d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
455a611cad6SBrian Somers 
45692e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
457e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
458e2ebb036SBrian Somers }
4596d666775SBrian Somers 
4606d666775SBrian Somers static void
4616d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4626d666775SBrian Somers {
4636d666775SBrian Somers   /* The given fsm is now up */
4646d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4656d666775SBrian Somers 
46692e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
467643f4904SBrian Somers     datalink_GotAuthname(dl, "", 0);
4683b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_ineed = dl->physical->link.lcp.want_auth;
4693b0f8d2eSBrian Somers     dl->physical->link.lcp.auth_iwait = dl->physical->link.lcp.his_auth;
4703b0f8d2eSBrian Somers     if (dl->physical->link.lcp.his_auth || dl->physical->link.lcp.want_auth) {
4713b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_ESTABLISH)
4725563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
473dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
4743b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.his_auth),
4753b0f8d2eSBrian Somers                 Auth2Nam(dl->physical->link.lcp.want_auth));
4763b0f8d2eSBrian Somers       if (dl->physical->link.lcp.his_auth == PROTO_PAP)
477dd7e2610SBrian Somers         auth_StartChallenge(&dl->pap, dl->physical, pap_SendChallenge);
4783b0f8d2eSBrian Somers       if (dl->physical->link.lcp.want_auth == PROTO_CHAP)
479dd7e2610SBrian Somers         auth_StartChallenge(&dl->chap.auth, dl->physical, chap_SendChallenge);
480e2ebb036SBrian Somers     } else
481e2ebb036SBrian Somers       datalink_AuthOk(dl);
482e2ebb036SBrian Somers   }
483e2ebb036SBrian Somers }
484e2ebb036SBrian Somers 
485e2ebb036SBrian Somers void
486643f4904SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name, int len)
487643f4904SBrian Somers {
488643f4904SBrian Somers   if (len >= sizeof dl->peer.authname)
489643f4904SBrian Somers     len = sizeof dl->peer.authname - 1;
490643f4904SBrian Somers   strncpy(dl->peer.authname, name, len);
491643f4904SBrian Somers   dl->peer.authname[len] = '\0';
492643f4904SBrian Somers }
493643f4904SBrian Somers 
494643f4904SBrian Somers void
49592b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
496e2ebb036SBrian Somers {
49706337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
4981df0a3b9SBrian Somers 
499dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5001fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5011fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5021fa665f5SBrian Somers       case MP_LINKSENT:
503ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5041fa665f5SBrian Somers         return;
5051fa665f5SBrian Somers       case MP_UP:
5060a1b5c9dSBrian Somers         /* First link in the bundle */
50792b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
5080a1b5c9dSBrian Somers         /* fall through */
5091fa665f5SBrian Somers       case MP_ADDED:
5100a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5111df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
5121fa665f5SBrian Somers         break;
5131fa665f5SBrian Somers       case MP_FAILED:
51449052c95SBrian Somers         datalink_AuthNotOk(dl);
51549052c95SBrian Somers         return;
51649052c95SBrian Somers     }
51749052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
518dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
519897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
520897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
52149052c95SBrian Somers     return;
52250abd4c8SBrian Somers   } else {
52350abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
524ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
52592b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
52650abd4c8SBrian Somers   }
52749052c95SBrian Somers 
52806337856SBrian Somers   if (ccpok) {
529dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
530dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
53106337856SBrian Somers   }
5329ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5333b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5343b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5356d666775SBrian Somers }
536e2ebb036SBrian Somers 
537e2ebb036SBrian Somers void
53892b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
53992b09558SBrian Somers {
54092b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
54192b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
54292b09558SBrian Somers }
54392b09558SBrian Somers 
54492b09558SBrian Somers void
54592b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
54692b09558SBrian Somers {
54792b09558SBrian Somers   cbcp_Down(&dl->cbcp);
54892b09558SBrian Somers   datalink_CBCPComplete(dl);
54992b09558SBrian Somers }
55092b09558SBrian Somers 
55192b09558SBrian Somers void
55292b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
55392b09558SBrian Somers {
55492b09558SBrian Somers   if (dl->physical->link.lcp.his_callback.opmask ==
55592b09558SBrian Somers       CALLBACK_BIT(CALLBACK_CBCP) ||
55692b09558SBrian Somers       dl->physical->link.lcp.want_callback.opmask ==
55792b09558SBrian Somers       CALLBACK_BIT(CALLBACK_CBCP)) {
55892b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
55992b09558SBrian Somers     cbcp_Up(&dl->cbcp);
56092b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
56192b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
56292b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
56392b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
56492b09558SBrian Somers   } else
56592b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
56692b09558SBrian Somers       case 0:
56792b09558SBrian Somers         datalink_NCPUp(dl);
56892b09558SBrian Somers         break;
56992b09558SBrian Somers 
57092b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
57192b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
57292b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
57392b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
57492b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
57592b09558SBrian Somers                      dl->peer.authname);
57692b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
57792b09558SBrian Somers         } else {
57892b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
57992b09558SBrian Somers           if (ptr)
58092b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
58192b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
58292b09558SBrian Somers                      dl->cbcp.fsm.phone);
58392b09558SBrian Somers           dl->cbcp.required = 1;
58492b09558SBrian Somers         }
58592b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
58692b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
58792b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
58892b09558SBrian Somers         break;
58992b09558SBrian Somers 
59092b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
59192b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
59292b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
59392b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
59492b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
59592b09558SBrian Somers                    dl->cbcp.fsm.phone);
59692b09558SBrian Somers         dl->cbcp.required = 1;
59792b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
59892b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
59992b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
60092b09558SBrian Somers         break;
60192b09558SBrian Somers 
60292b09558SBrian Somers       default:
60392b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
60492b09558SBrian Somers                    dl->name);
60592b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
60692b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
60792b09558SBrian Somers         break;
60892b09558SBrian Somers     }
60992b09558SBrian Somers }
61092b09558SBrian Somers 
61192b09558SBrian Somers void
612e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
613e2ebb036SBrian Somers {
6149ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
615dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6166d666775SBrian Somers }
6176d666775SBrian Somers 
6186d666775SBrian Somers static void
6196d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6206d666775SBrian Somers {
6216d666775SBrian Somers   /* The given FSM has been told to come down */
6226d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
623a611cad6SBrian Somers 
62492e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
625e2ebb036SBrian Somers     switch (dl->state) {
626e2ebb036SBrian Somers       case DATALINK_OPEN:
627643f4904SBrian Somers         peerid_Init(&dl->peer);
62809206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
629ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
630e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
63192b09558SBrian Somers         /* fall through (just in case) */
63292b09558SBrian Somers 
63392b09558SBrian Somers       case DATALINK_CBCP:
63492b09558SBrian Somers         if (!dl->cbcp.required)
63592b09558SBrian Somers           cbcp_Down(&dl->cbcp);
63692b09558SBrian Somers         /* fall through (just in case) */
637e2ebb036SBrian Somers 
638e2ebb036SBrian Somers       case DATALINK_AUTH:
639dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
640dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
6416d666775SBrian Somers     }
6429ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
643e2ebb036SBrian Somers   }
6446d666775SBrian Somers }
6456d666775SBrian Somers 
6466d666775SBrian Somers static void
6476d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
6486d666775SBrian Somers {
6496d666775SBrian Somers   /* The given fsm is now down */
6506d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
6516d666775SBrian Somers 
65292e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
65309206a6fSBrian Somers     fsm2initial(fp);
6546d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
6559c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
6560a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
6570a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
6586d666775SBrian Somers }
6596d666775SBrian Somers 
6603006ec67SBrian Somers struct datalink *
6616f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
6623006ec67SBrian Somers {
6633006ec67SBrian Somers   struct datalink *dl;
6643006ec67SBrian Somers 
6653006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
6663006ec67SBrian Somers   if (dl == NULL)
6673006ec67SBrian Somers     return dl;
6683006ec67SBrian Somers 
6693006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
6703006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
6713006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
6723006ec67SBrian Somers   dl->desc.Read = datalink_Read;
6733006ec67SBrian Somers   dl->desc.Write = datalink_Write;
6745b8b8060SBrian Somers 
675e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
676e718d1d7SBrian Somers 
67773a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
67873a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
67973a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
680f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
681f9545805SBrian Somers   *dl->phone.list = '\0';
682f9545805SBrian Somers   dl->phone.next = NULL;
683f9545805SBrian Somers   dl->phone.alt = NULL;
684f9545805SBrian Somers   dl->phone.chosen = "N/A";
6859c81b87dSBrian Somers   dl->stayonline = 0;
686c7cc5030SBrian Somers   dl->script.run = 1;
687c7cc5030SBrian Somers   dl->script.packetmode = 1;
6883b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
6895b8b8060SBrian Somers 
6903006ec67SBrian Somers   dl->bundle = bundle;
6913006ec67SBrian Somers   dl->next = NULL;
692e718d1d7SBrian Somers 
6933006ec67SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
694e718d1d7SBrian Somers 
6953006ec67SBrian Somers   dl->dial_tries = 0;
696565e35e5SBrian Somers   dl->cfg.dial.max = 1;
697565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
698565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
699e718d1d7SBrian Somers 
700abff9baeSBrian Somers   dl->reconnect_tries = 0;
701565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
702565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
703abff9baeSBrian Somers 
70492b09558SBrian Somers   dl->cfg.callback.opmask = 0;
70592b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
70692b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
70792b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
70892b09558SBrian Somers 
7093006ec67SBrian Somers   dl->name = strdup(name);
710643f4904SBrian Somers   peerid_Init(&dl->peer);
7116f384573SBrian Somers   dl->parent = &bundle->fsm;
7123b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7133b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7143b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7153b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7163b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7173b0f8d2eSBrian Somers 
718dd7e2610SBrian Somers   auth_Init(&dl->pap);
719dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
7203b0f8d2eSBrian Somers 
721565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
7223006ec67SBrian Somers     free(dl->name);
7233006ec67SBrian Somers     free(dl);
7243006ec67SBrian Somers     return NULL;
7253006ec67SBrian Somers   }
72692b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
727f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
7283006ec67SBrian Somers 
7299ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
7309ae58882SBrian Somers              dl->name, datalink_State(dl));
7313006ec67SBrian Somers 
7323006ec67SBrian Somers   return dl;
7333006ec67SBrian Somers }
7343006ec67SBrian Somers 
7353006ec67SBrian Somers struct datalink *
736cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
737cd7bd93aSBrian Somers {
738cd7bd93aSBrian Somers   struct datalink *dl;
739cd7bd93aSBrian Somers 
740cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
741cd7bd93aSBrian Somers   if (dl == NULL)
742cd7bd93aSBrian Somers     return dl;
743cd7bd93aSBrian Somers 
744cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
745cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
746cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
747cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
748cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
749cd7bd93aSBrian Somers 
750cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
751cd7bd93aSBrian Somers 
752cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
753cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
754cd7bd93aSBrian Somers   *dl->phone.list = '\0';
755643f4904SBrian Somers   dl->phone.next = NULL;
756643f4904SBrian Somers   dl->phone.alt = NULL;
757643f4904SBrian Somers   dl->phone.chosen = "N/A";
758cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
759cd7bd93aSBrian Somers   dl->next = NULL;
760cd7bd93aSBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
761cd7bd93aSBrian Somers   dl->dial_tries = 0;
762cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
763cd7bd93aSBrian Somers   dl->name = strdup(name);
764643f4904SBrian Somers   peerid_Init(&dl->peer);
765cd7bd93aSBrian Somers   dl->parent = odl->parent;
766cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
767643f4904SBrian Somers   dl->fsmp.object = dl;
768dd7e2610SBrian Somers   auth_Init(&dl->pap);
769cd7bd93aSBrian Somers   dl->pap.cfg.fsmretry = odl->pap.cfg.fsmretry;
770cd7bd93aSBrian Somers 
771dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
772cd7bd93aSBrian Somers   dl->chap.auth.cfg.fsmretry = odl->chap.auth.cfg.fsmretry;
773cd7bd93aSBrian Somers 
77481358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
775cd7bd93aSBrian Somers     free(dl->name);
776cd7bd93aSBrian Somers     free(dl);
777cd7bd93aSBrian Somers     return NULL;
778cd7bd93aSBrian Somers   }
779cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
780cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
781cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
782cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
783cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
784cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
785cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
786cd7bd93aSBrian Somers 
78792b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
788cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
789cd7bd93aSBrian Somers 
7909ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
7919ae58882SBrian Somers              dl->name, datalink_State(dl));
792cd7bd93aSBrian Somers 
793cd7bd93aSBrian Somers   return dl;
794cd7bd93aSBrian Somers }
795cd7bd93aSBrian Somers 
796cd7bd93aSBrian Somers struct datalink *
7973006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
7983006ec67SBrian Somers {
7993006ec67SBrian Somers   struct datalink *result;
8003006ec67SBrian Somers 
80139d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
802dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
803c7cc5030SBrian Somers               datalink_State(dl));
80439d94652SBrian Somers     switch (dl->state) {
80539d94652SBrian Somers       case DATALINK_HANGUP:
80639d94652SBrian Somers       case DATALINK_DIAL:
80739d94652SBrian Somers       case DATALINK_LOGIN:
80839d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
80939d94652SBrian Somers         break;
81039d94652SBrian Somers     }
81139d94652SBrian Somers   }
8123006ec67SBrian Somers 
8133006ec67SBrian Somers   result = dl->next;
8143b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
8153006ec67SBrian Somers   free(dl->name);
8163006ec67SBrian Somers   free(dl);
8173006ec67SBrian Somers 
8183006ec67SBrian Somers   return result;
8193006ec67SBrian Somers }
8203006ec67SBrian Somers 
8213006ec67SBrian Somers void
822c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8233006ec67SBrian Somers {
8246f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
825565e35e5SBrian Somers     /* Ignore scripts */
826565e35e5SBrian Somers     runscripts = 0;
827565e35e5SBrian Somers 
828c7cc5030SBrian Somers   switch (dl->state) {
829c7cc5030SBrian Somers     case DATALINK_CLOSED:
8303b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
8313b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
8323b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
8339ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
834565e35e5SBrian Somers       dl->reconnect_tries =
8356f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
836565e35e5SBrian Somers       dl->dial_tries = dl->cfg.dial.max;
837c5a5a6caSBrian Somers       dl->script.run = runscripts;
838c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
839c7cc5030SBrian Somers       break;
840c7cc5030SBrian Somers 
841c7cc5030SBrian Somers     case DATALINK_OPENING:
842c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
843c7cc5030SBrian Somers         dl->script.run = 1;
844c7cc5030SBrian Somers       /* fall through */
845c7cc5030SBrian Somers 
846c7cc5030SBrian Somers     case DATALINK_DIAL:
847c7cc5030SBrian Somers     case DATALINK_LOGIN:
848c7cc5030SBrian Somers     case DATALINK_READY:
849c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
850c7cc5030SBrian Somers         dl->script.packetmode = 1;
851c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
852c7cc5030SBrian Somers           datalink_LoginDone(dl);
853c7cc5030SBrian Somers       }
854c7cc5030SBrian Somers       break;
8553006ec67SBrian Somers   }
8563006ec67SBrian Somers }
8573006ec67SBrian Somers 
8583006ec67SBrian Somers void
8599c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
860c7cc5030SBrian Somers {
861c7cc5030SBrian Somers   /* Please close */
862e2ebb036SBrian Somers   switch (dl->state) {
863e2ebb036SBrian Somers     case DATALINK_OPEN:
864643f4904SBrian Somers       peerid_Init(&dl->peer);
86509206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
866e2ebb036SBrian Somers       /* fall through */
867e2ebb036SBrian Somers 
86892b09558SBrian Somers     case DATALINK_CBCP:
869e2ebb036SBrian Somers     case DATALINK_AUTH:
870e2ebb036SBrian Somers     case DATALINK_LCP:
871dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
8729c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
873c7cc5030SBrian Somers         dl->dial_tries = -1;
874c7cc5030SBrian Somers         dl->reconnect_tries = 0;
8759c81b87dSBrian Somers         if (how == CLOSE_LCP)
8769c81b87dSBrian Somers           dl->stayonline = 1;
877c7cc5030SBrian Somers       }
878e2ebb036SBrian Somers       break;
879e2ebb036SBrian Somers 
880e2ebb036SBrian Somers     default:
8819c81b87dSBrian Somers       datalink_ComeDown(dl, how);
882c7cc5030SBrian Somers   }
883e2ebb036SBrian Somers }
884c7cc5030SBrian Somers 
885c7cc5030SBrian Somers void
8869c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
887c7cc5030SBrian Somers {
888c7cc5030SBrian Somers   /* Carrier is lost */
889e2ebb036SBrian Somers   switch (dl->state) {
890e2ebb036SBrian Somers     case DATALINK_OPEN:
891643f4904SBrian Somers       peerid_Init(&dl->peer);
89209206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
893e2ebb036SBrian Somers       /* fall through */
894e2ebb036SBrian Somers 
89592b09558SBrian Somers     case DATALINK_CBCP:
896e2ebb036SBrian Somers     case DATALINK_AUTH:
897e2ebb036SBrian Somers     case DATALINK_LCP:
89809206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
899e2ebb036SBrian Somers       /* fall through */
900c7cc5030SBrian Somers 
901e2ebb036SBrian Somers     default:
9029c81b87dSBrian Somers       datalink_ComeDown(dl, how);
903c7cc5030SBrian Somers   }
904e2ebb036SBrian Somers }
905c7cc5030SBrian Somers 
906c7cc5030SBrian Somers void
9073006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9083006ec67SBrian Somers {
9093006ec67SBrian Somers   dl->reconnect_tries = 0;
9103006ec67SBrian Somers }
911c7cc5030SBrian Somers 
9129c81b87dSBrian Somers void
9139c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9149c81b87dSBrian Somers {
9157729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9169c81b87dSBrian Somers     dl->stayonline = 1;
9179c81b87dSBrian Somers }
9189c81b87dSBrian Somers 
919643f4904SBrian Somers int
920643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
921c7cc5030SBrian Somers {
922643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
923643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
924643f4904SBrian Somers                 datalink_State(arg->cx));
925643f4904SBrian Somers   prompt_Printf(arg->prompt, " CHAP Encryption:    %s\n",
926643f4904SBrian Somers                 arg->cx->chap.using_MSChap ? "MSChap" : "MD5" );
927643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
928643f4904SBrian Somers   if (*arg->cx->peer.authname)
929643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
930643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
931643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
932565e35e5SBrian Somers   else
933643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
934643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
935643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
936643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
937643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
938643f4904SBrian Somers 
939643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
940643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
941643f4904SBrian Somers                 arg->cx->cfg.phone.list);
942643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
943643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
944643f4904SBrian Somers                   arg->cx->cfg.dial.max);
945565e35e5SBrian Somers   else
946643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
947643f4904SBrian Somers   if (arg->cx->cfg.dial.next_timeout > 0)
948643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
949565e35e5SBrian Somers   else
950643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
951643f4904SBrian Somers   if (arg->cx->cfg.dial.timeout > 0)
952643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
953565e35e5SBrian Somers   else
954643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
955643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
956643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
957643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
958643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
959643f4904SBrian Somers   else
960643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
96192b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
96292b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
96392b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
96492b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
96592b09558SBrian Somers   else {
96692b09558SBrian Somers     int comma = 0;
96792b09558SBrian Somers 
96892b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
96992b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
97092b09558SBrian Somers       comma = 1;
97192b09558SBrian Somers     }
97292b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
97392b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
97492b09558SBrian Somers       comma = 1;
97592b09558SBrian Somers     }
97692b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
97792b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
97892b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
97992b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
98092b09558SBrian Somers       comma = 1;
98192b09558SBrian Somers     }
98292b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
98392b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
98492b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
98592b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
98692b09558SBrian Somers       prompt_Printf(arg->prompt, "                     phone: %s\n",
98792b09558SBrian Somers                     arg->cx->cfg.cbcp.phone);
98892b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
98992b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
99092b09558SBrian Somers     } else
99192b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
99292b09558SBrian Somers   }
99392b09558SBrian Somers 
994643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
995643f4904SBrian Somers                 arg->cx->cfg.script.dial);
996643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
997643f4904SBrian Somers                 arg->cx->cfg.script.login);
998643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
999643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1000643f4904SBrian Somers   return 0;
1001565e35e5SBrian Somers }
1002565e35e5SBrian Somers 
1003565e35e5SBrian Somers int
1004565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1005565e35e5SBrian Somers {
100625092092SBrian Somers   if (arg->argc == arg->argn+2) {
100725092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
100825092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1009565e35e5SBrian Somers     return 0;
1010565e35e5SBrian Somers   }
1011565e35e5SBrian Somers   return -1;
1012565e35e5SBrian Somers }
1013565e35e5SBrian Somers 
1014565e35e5SBrian Somers int
1015565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1016565e35e5SBrian Somers {
1017565e35e5SBrian Somers   int timeout;
1018565e35e5SBrian Somers   int tries;
1019565e35e5SBrian Somers   char *dot;
1020565e35e5SBrian Somers 
102125092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
102225092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
102325092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1024565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1025565e35e5SBrian Somers       randinit();
1026565e35e5SBrian Somers     } else {
102725092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1028565e35e5SBrian Somers 
1029565e35e5SBrian Somers       if (timeout >= 0)
1030565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1031565e35e5SBrian Somers       else {
1032dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1033565e35e5SBrian Somers 	return -1;
1034565e35e5SBrian Somers       }
1035565e35e5SBrian Somers     }
1036565e35e5SBrian Somers 
103725092092SBrian Somers     dot = strchr(arg->argv[arg->argn], '.');
1038565e35e5SBrian Somers     if (dot) {
1039565e35e5SBrian Somers       if (strcasecmp(++dot, "random") == 0) {
1040565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1041565e35e5SBrian Somers 	randinit();
1042565e35e5SBrian Somers       } else {
1043565e35e5SBrian Somers 	timeout = atoi(dot);
1044565e35e5SBrian Somers 	if (timeout >= 0)
1045565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1046565e35e5SBrian Somers 	else {
1047dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1048565e35e5SBrian Somers 	  return -1;
1049565e35e5SBrian Somers 	}
1050565e35e5SBrian Somers       }
1051565e35e5SBrian Somers     } else
1052565e35e5SBrian Somers       /* Default next timeout */
1053565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1054565e35e5SBrian Somers 
105525092092SBrian Somers     if (arg->argc == arg->argn+2) {
105625092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1057565e35e5SBrian Somers 
1058565e35e5SBrian Somers       if (tries >= 0) {
1059565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1060565e35e5SBrian Somers       } else {
1061dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1062565e35e5SBrian Somers 	return 1;
1063565e35e5SBrian Somers       }
1064565e35e5SBrian Somers     }
1065565e35e5SBrian Somers     return 0;
1066565e35e5SBrian Somers   }
1067565e35e5SBrian Somers   return -1;
1068c7cc5030SBrian Somers }
1069c7cc5030SBrian Somers 
1070cdbbb6b5SBrian Somers static const char *states[] = {
1071565e35e5SBrian Somers   "closed",
1072565e35e5SBrian Somers   "opening",
1073565e35e5SBrian Somers   "hangup",
1074565e35e5SBrian Somers   "dial",
1075565e35e5SBrian Somers   "login",
1076565e35e5SBrian Somers   "ready",
1077565e35e5SBrian Somers   "lcp",
1078565e35e5SBrian Somers   "auth",
107992b09558SBrian Somers   "cbcp",
1080565e35e5SBrian Somers   "open"
1081c7cc5030SBrian Somers };
1082c7cc5030SBrian Somers 
1083643f4904SBrian Somers const char *
1084c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1085c7cc5030SBrian Somers {
1086c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1087c7cc5030SBrian Somers     return "unknown";
1088c7cc5030SBrian Somers   return states[dl->state];
1089c7cc5030SBrian Somers }
10906f384573SBrian Somers 
10919ae58882SBrian Somers static void
10929ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
10939ae58882SBrian Somers {
10949ae58882SBrian Somers   if (state != dl->state) {
10959ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
10969ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
10979ae58882SBrian Somers                  states[state]);
10989ae58882SBrian Somers       dl->state = state;
10999ae58882SBrian Somers     } else
11009ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
11019ae58882SBrian Somers   }
11029ae58882SBrian Somers }
11039ae58882SBrian Somers 
11046f384573SBrian Somers struct datalink *
110596c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
110696c9bb21SBrian Somers              int fd)
11076f384573SBrian Somers {
1108b7c5748eSBrian Somers   struct datalink *dl, *cdl;
11096f384573SBrian Somers   u_int retry;
1110b7c5748eSBrian Somers   char *oname;
11116f384573SBrian Somers 
111296c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
111396c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
11146f384573SBrian Somers 
111596c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
111696c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
111796c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
111896c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
11196f384573SBrian Somers   }
1120b7c5748eSBrian Somers 
1121b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1122b7c5748eSBrian Somers   oname = NULL;
1123b7c5748eSBrian Somers   do {
1124b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1125b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1126b7c5748eSBrian Somers         if (oname)
1127b7c5748eSBrian Somers           free(datalink_NextName(dl));
1128b7c5748eSBrian Somers         else
1129b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1130b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1131b7c5748eSBrian Somers       }
1132b7c5748eSBrian Somers   } while (cdl);
1133b7c5748eSBrian Somers 
1134b7c5748eSBrian Somers   if (oname) {
1135b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1136b7c5748eSBrian Somers     free(oname);
1137b7c5748eSBrian Somers   } else {
113896c9bb21SBrian Somers     dl->name = strdup(dl->name);
1139b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1140b7c5748eSBrian Somers   }
1141b7c5748eSBrian Somers   (*niov)++;
11426f384573SBrian Somers 
11436f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
11446f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
11456f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
11466f384573SBrian Somers   dl->desc.Read = datalink_Read;
11476f384573SBrian Somers   dl->desc.Write = datalink_Write;
11486f384573SBrian Somers 
11496f384573SBrian Somers   mp_linkInit(&dl->mp);
11506f384573SBrian Somers   *dl->phone.list = '\0';
11516f384573SBrian Somers   dl->phone.next = NULL;
11526f384573SBrian Somers   dl->phone.alt = NULL;
11536f384573SBrian Somers   dl->phone.chosen = "N/A";
11546f384573SBrian Somers 
11556f384573SBrian Somers   dl->bundle = bundle;
11566f384573SBrian Somers   dl->next = NULL;
11576f384573SBrian Somers   memset(&dl->dial_timer, '\0', sizeof dl->dial_timer);
11586f384573SBrian Somers   dl->dial_tries = 0;
11596f384573SBrian Somers   dl->reconnect_tries = 0;
11606f384573SBrian Somers   dl->parent = &bundle->fsm;
11616f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
11626f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
11636f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
11646f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
11656f384573SBrian Somers   dl->fsmp.object = dl;
11666f384573SBrian Somers 
11676f384573SBrian Somers   retry = dl->pap.cfg.fsmretry;
1168dd7e2610SBrian Somers   auth_Init(&dl->pap);
11696f384573SBrian Somers   dl->pap.cfg.fsmretry = retry;
11706f384573SBrian Somers 
11716f384573SBrian Somers   retry = dl->chap.auth.cfg.fsmretry;
1172dd7e2610SBrian Somers   auth_Init(&dl->chap.auth);
11736f384573SBrian Somers   dl->chap.auth.cfg.fsmretry = retry;
11746f384573SBrian Somers 
117596c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
117696c9bb21SBrian Somers 
117796c9bb21SBrian Somers   if (!dl->physical) {
11786f384573SBrian Somers     free(dl->name);
11796f384573SBrian Somers     free(dl);
11806f384573SBrian Somers     dl = NULL;
11819ae58882SBrian Somers   } else {
118292b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
11836f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
11846f384573SBrian Somers 
11859ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
11869ae58882SBrian Somers               dl->name, datalink_State(dl));
11879ae58882SBrian Somers   }
11889ae58882SBrian Somers 
11896f384573SBrian Somers   return dl;
11906f384573SBrian Somers }
11916f384573SBrian Somers 
11926f384573SBrian Somers int
119385fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
119485fd273aSBrian Somers              pid_t newpid)
11956f384573SBrian Somers {
119696c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
119796c9bb21SBrian Somers   int link_fd;
11986f384573SBrian Somers 
119996c9bb21SBrian Somers   if (dl) {
1200dd7e2610SBrian Somers     timer_Stop(&dl->dial_timer);
120192b09558SBrian Somers     /* The following is purely for the sake of paranoia */
120292b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1203dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1204dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
12056f384573SBrian Somers   }
12066f384573SBrian Somers 
120796c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
120896c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
120996c9bb21SBrian Somers     if (dl) {
12106f384573SBrian Somers       free(dl->name);
12116f384573SBrian Somers       free(dl);
121296c9bb21SBrian Somers     }
121396c9bb21SBrian Somers     return -1;
121496c9bb21SBrian Somers   }
121596c9bb21SBrian Somers 
121696c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
121796c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
121896c9bb21SBrian Somers   iov[*niov].iov_base =
121996c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
122096c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
122196c9bb21SBrian Somers 
122285fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
122396c9bb21SBrian Somers 
122496c9bb21SBrian Somers   if (link_fd == -1 && dl) {
122596c9bb21SBrian Somers     free(dl->name);
122696c9bb21SBrian Somers     free(dl);
122796c9bb21SBrian Somers   }
12286f384573SBrian Somers 
12296f384573SBrian Somers   return link_fd;
12306f384573SBrian Somers }
12316f384573SBrian Somers 
123258d55334SBrian Somers void
123358d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
123458d55334SBrian Somers {
123558d55334SBrian Somers   free(dl->name);
123658d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
123758d55334SBrian Somers }
123858d55334SBrian Somers 
123984917b87SBrian Somers char *
124084917b87SBrian Somers datalink_NextName(struct datalink *dl)
12416f384573SBrian Somers {
12426f384573SBrian Somers   int f, n;
124384917b87SBrian Somers   char *name, *oname;
12446f384573SBrian Somers 
12456f384573SBrian Somers   n = strlen(dl->name);
12466f384573SBrian Somers   name = (char *)malloc(n+3);
12476f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
12486f384573SBrian Somers     if (!isdigit(dl->name[f]))
12496f384573SBrian Somers       break;
12506f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
12516f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
125284917b87SBrian Somers   oname = dl->name;
125354cd8e13SBrian Somers   dl->name = name;
125454cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
125584917b87SBrian Somers   return oname;
12566f384573SBrian Somers }
1257dd0645c5SBrian Somers 
1258dd0645c5SBrian Somers int
1259dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1260dd0645c5SBrian Somers {
1261dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1262dd0645c5SBrian Somers     return 0;
1263dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1264dd0645c5SBrian Somers     dl->script.run = 0;
1265dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1266dd0645c5SBrian Somers     dl->reconnect_tries = 0;
126781358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1268dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1269dd0645c5SBrian Somers   return 1;
1270dd0645c5SBrian Somers }
1271