xref: /freebsd/usr.sbin/ppp/datalink.c (revision eb6e5e05)
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  *
26eb6e5e05SBrian Somers  *	$Id: datalink.c,v 1.42 1999/08/05 10:32:10 brian Exp $
273006ec67SBrian Somers  */
283006ec67SBrian Somers 
29972a1bcfSBrian Somers #include <sys/param.h>
303006ec67SBrian Somers #include <netinet/in.h>
31eaa4df37SBrian Somers #include <netinet/in_systm.h>
32eaa4df37SBrian Somers #include <netinet/ip.h>
331fa665f5SBrian Somers #include <sys/un.h>
343006ec67SBrian Somers 
356f384573SBrian Somers #include <ctype.h>
36c7cc5030SBrian Somers #include <stdio.h>
373006ec67SBrian Somers #include <stdlib.h>
383006ec67SBrian Somers #include <string.h>
3996c9bb21SBrian Somers #include <sys/uio.h>
403006ec67SBrian Somers #include <termios.h>
413006ec67SBrian Somers 
425d9e6103SBrian Somers #include "layer.h"
433006ec67SBrian Somers #include "mbuf.h"
443006ec67SBrian Somers #include "log.h"
453006ec67SBrian Somers #include "defs.h"
463006ec67SBrian Somers #include "timer.h"
473006ec67SBrian Somers #include "fsm.h"
483006ec67SBrian Somers #include "lcp.h"
493006ec67SBrian Somers #include "descriptor.h"
50879ed6faSBrian Somers #include "lqr.h"
513006ec67SBrian Somers #include "hdlc.h"
523006ec67SBrian Somers #include "async.h"
533006ec67SBrian Somers #include "throughput.h"
543b0f8d2eSBrian Somers #include "ccp.h"
553006ec67SBrian Somers #include "link.h"
563006ec67SBrian Somers #include "physical.h"
575828db6dSBrian Somers #include "iplist.h"
58eaa4df37SBrian Somers #include "slcompress.h"
595828db6dSBrian Somers #include "ipcp.h"
605ca5389aSBrian Somers #include "filter.h"
613b0f8d2eSBrian Somers #include "mp.h"
62972a1bcfSBrian Somers #ifndef NORADIUS
63972a1bcfSBrian Somers #include "radius.h"
64972a1bcfSBrian Somers #endif
653006ec67SBrian Somers #include "bundle.h"
663006ec67SBrian Somers #include "chat.h"
67e2ebb036SBrian Somers #include "auth.h"
68c7cc5030SBrian Somers #include "prompt.h"
695d9e6103SBrian Somers #include "proto.h"
70e2ebb036SBrian Somers #include "pap.h"
71e2ebb036SBrian Somers #include "chap.h"
72565e35e5SBrian Somers #include "command.h"
7392b09558SBrian Somers #include "cbcp.h"
74e2ebb036SBrian Somers #include "datalink.h"
75c7cc5030SBrian Somers 
76030e4ebbSBrian Somers static void datalink_LoginDone(struct datalink *);
779ae58882SBrian Somers static void datalink_NewState(struct datalink *, int);
783006ec67SBrian Somers 
793006ec67SBrian Somers static void
803006ec67SBrian Somers datalink_OpenTimeout(void *v)
813006ec67SBrian Somers {
823006ec67SBrian Somers   struct datalink *dl = (struct datalink *)v;
833006ec67SBrian Somers 
84c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
853006ec67SBrian Somers   if (dl->state == DATALINK_OPENING)
86dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name);
873006ec67SBrian Somers }
883006ec67SBrian Somers 
89b5c347a3SBrian Somers static int
903006ec67SBrian Somers datalink_StartDialTimer(struct datalink *dl, int Timeout)
913006ec67SBrian Somers {
92b5c347a3SBrian Somers   int result = Timeout;
933006ec67SBrian Somers 
94c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
953006ec67SBrian Somers   if (Timeout) {
963006ec67SBrian Somers     if (Timeout > 0)
97c11e57a3SBrian Somers       dl->dial.timer.load = Timeout * SECTICKS;
98b5c347a3SBrian Somers     else {
99b5c347a3SBrian Somers       result = (random() % DIAL_TIMEOUT) + 1;
100c11e57a3SBrian Somers       dl->dial.timer.load = result * SECTICKS;
101b5c347a3SBrian Somers     }
102c11e57a3SBrian Somers     dl->dial.timer.func = datalink_OpenTimeout;
103c11e57a3SBrian Somers     dl->dial.timer.name = "dial";
104c11e57a3SBrian Somers     dl->dial.timer.arg = dl;
105c11e57a3SBrian Somers     timer_Start(&dl->dial.timer);
1063006ec67SBrian Somers     if (dl->state == DATALINK_OPENING)
107dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
1083006ec67SBrian Somers                 dl->name, Timeout);
1093006ec67SBrian Somers   }
110b5c347a3SBrian Somers   return result;
1113006ec67SBrian Somers }
1123006ec67SBrian Somers 
1133006ec67SBrian Somers static void
1143006ec67SBrian Somers datalink_HangupDone(struct datalink *dl)
1153006ec67SBrian Somers {
116030e4ebbSBrian Somers   if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
1175d9e6103SBrian Somers       dl->physical->fd != -1) {
1185d9e6103SBrian Somers     /* Don't close our device if the link is dedicated */
119030e4ebbSBrian Somers     datalink_LoginDone(dl);
120030e4ebbSBrian Somers     return;
121030e4ebbSBrian Somers   }
122030e4ebbSBrian Somers 
1235d9e6103SBrian Somers   physical_Close(dl->physical);
124565e35e5SBrian Somers   dl->phone.chosen = "N/A";
1253006ec67SBrian Somers 
12692b09558SBrian Somers   if (dl->cbcp.required) {
12792b09558SBrian Somers     log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
12892b09558SBrian Somers     dl->cfg.callback.opmask = 0;
12992b09558SBrian Somers     strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
13092b09558SBrian Somers             sizeof dl->cfg.phone.list - 1);
13192b09558SBrian Somers     dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
13292b09558SBrian Somers     dl->phone.alt = dl->phone.next = NULL;
13392b09558SBrian Somers     dl->reconnect_tries = dl->cfg.reconnect.max;
134c11e57a3SBrian Somers     dl->dial.tries = dl->cfg.dial.max;
135c11e57a3SBrian Somers     dl->dial.incs = 0;
13692b09558SBrian Somers     dl->script.run = 1;
13792b09558SBrian Somers     dl->script.packetmode = 1;
13892b09558SBrian Somers     if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
13992b09558SBrian Somers       log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
14092b09558SBrian Somers     bundle_LinksRemoved(dl->bundle);
141c11e57a3SBrian Somers     /* if dial.timeout is < 0 (random), we don't override fsm.delay */
14292b09558SBrian Somers     if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
14392b09558SBrian Somers       dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
14492b09558SBrian Somers     datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
14592b09558SBrian Somers     cbcp_Down(&dl->cbcp);
14692b09558SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
1470ca6f91bSBrian Somers     if (bundle_Phase(dl->bundle) != PHASE_TERMINATE)
1480ca6f91bSBrian Somers       bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
14992b09558SBrian Somers   } else if (dl->bundle->CleaningUp ||
1506f384573SBrian Somers       (dl->physical->type == PHYS_DIRECT) ||
151c11e57a3SBrian Somers       ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15281358fa3SBrian Somers        !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
1539ae58882SBrian Somers     datalink_NewState(dl, DATALINK_CLOSED);
154c11e57a3SBrian Somers     dl->dial.tries = -1;
155c11e57a3SBrian Somers     dl->dial.incs = 0;
1563006ec67SBrian Somers     dl->reconnect_tries = 0;
1573006ec67SBrian Somers     bundle_LinkClosed(dl->bundle, dl);
1583b0f8d2eSBrian Somers     if (!dl->bundle->CleaningUp)
159c11e57a3SBrian Somers       datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1603006ec67SBrian Somers   } else {
1619ae58882SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
1620ca6f91bSBrian Somers     if (bundle_Phase(dl->bundle) != PHASE_TERMINATE)
1630ca6f91bSBrian Somers       bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
164c11e57a3SBrian Somers     if (dl->dial.tries < 0) {
165565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
166c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
167c11e57a3SBrian Somers       dl->dial.incs = 0;
168abff9baeSBrian Somers       dl->reconnect_tries--;
169abff9baeSBrian Somers     } else {
170f9545805SBrian Somers       if (dl->phone.next == NULL)
171c11e57a3SBrian Somers         datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1723006ec67SBrian Somers       else
173565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
1743006ec67SBrian Somers     }
175abff9baeSBrian Somers   }
176abff9baeSBrian Somers }
1773006ec67SBrian Somers 
178eb6e5e05SBrian Somers const char *
179f9545805SBrian Somers datalink_ChoosePhoneNumber(struct datalink *dl)
180f9545805SBrian Somers {
181f9545805SBrian Somers   char *phone;
182f9545805SBrian Somers 
183f9545805SBrian Somers   if (dl->phone.alt == NULL) {
184f9545805SBrian Somers     if (dl->phone.next == NULL) {
185f9545805SBrian Somers       strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
186f9545805SBrian Somers       dl->phone.list[sizeof dl->phone.list - 1] = '\0';
18708da4867SBrian Somers       if (*dl->phone.list == '\0')
18808da4867SBrian Somers         return "";
189f9545805SBrian Somers       dl->phone.next = dl->phone.list;
190f9545805SBrian Somers     }
191f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
192f9545805SBrian Somers   }
193f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
194f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
195f9545805SBrian Somers   if (*phone)
196dd7e2610SBrian Somers     log_Printf(LogPHASE, "Phone: %s\n", phone);
197f9545805SBrian Somers   return phone;
198f9545805SBrian Somers }
199f9545805SBrian Somers 
200c5a5a6caSBrian Somers static void
201c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
202c5a5a6caSBrian Somers {
203d345321bSBrian Somers   if (!dl->script.packetmode) {
204c11e57a3SBrian Somers     dl->dial.tries = -1;
205c11e57a3SBrian Somers     dl->dial.incs = 0;
2069ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
2075d9e6103SBrian Somers   } else if (!physical_Raw(dl->physical)) {
208c11e57a3SBrian Somers     dl->dial.tries = 0;
209dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
210c5a5a6caSBrian Somers     if (dl->script.run) {
2119ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
2125d9e6103SBrian Somers       physical_Offline(dl->physical);
213f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
214030e4ebbSBrian Somers     } else {
2156815097bSBrian Somers       physical_StopDeviceTimer(dl->physical);
216030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
217030e4ebbSBrian Somers         /* force a redial timeout */
2185d9e6103SBrian Somers         physical_Close(dl->physical);
219c5a5a6caSBrian Somers       datalink_HangupDone(dl);
220030e4ebbSBrian Somers     }
221c5a5a6caSBrian Somers   } else {
222c11e57a3SBrian Somers     dl->dial.tries = -1;
223c11e57a3SBrian Somers     dl->dial.incs = 0;
224c7cc5030SBrian Somers 
225643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
2263b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
2273b0f8d2eSBrian Somers 
228cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
229cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
2303b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
231503a7782SBrian Somers 
2329ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
233dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
234dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
235c5a5a6caSBrian Somers   }
236c5a5a6caSBrian Somers }
237c5a5a6caSBrian Somers 
2383006ec67SBrian Somers static int
2393006ec67SBrian Somers datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
2403006ec67SBrian Somers                    int *n)
2413006ec67SBrian Somers {
2423006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2433006ec67SBrian Somers   int result;
2443006ec67SBrian Somers 
245310c3babSBrian Somers   result = 0;
2463006ec67SBrian Somers   switch (dl->state) {
2473006ec67SBrian Somers     case DATALINK_CLOSED:
24881358fa3SBrian Somers       if ((dl->physical->type &
24981358fa3SBrian Somers            (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
250194c225dSBrian Somers           !dl->bundle->CleaningUp)
251565e35e5SBrian Somers         /*
25281358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
25381358fa3SBrian Somers          * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
25481358fa3SBrian Somers          * Go to DATALINK_OPENING via datalink_Up() and fall through.
255565e35e5SBrian Somers          */
256565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
257565e35e5SBrian Somers       else
258310c3babSBrian Somers         break;
259565e35e5SBrian Somers       /* fall through */
2603006ec67SBrian Somers 
2613006ec67SBrian Somers     case DATALINK_OPENING:
262c11e57a3SBrian Somers       if (dl->dial.timer.state != TIMER_RUNNING) {
263c11e57a3SBrian Somers         if (--dl->dial.tries < 0)
264c11e57a3SBrian Somers           dl->dial.tries = 0;
2655d9e6103SBrian Somers         if (physical_Open(dl->physical, dl->bundle) >= 0) {
266bf1d3ff6SBrian Somers           log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
267bf1d3ff6SBrian Somers                            "Type `~?' for help\r\n", dl->name,
268bf1d3ff6SBrian Somers                            dl->physical->name.full);
269c5a5a6caSBrian Somers           if (dl->script.run) {
2709ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
271f9545805SBrian Somers             chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
272eb6e5e05SBrian Somers                       *dl->cfg.script.dial ?
273eb6e5e05SBrian Somers                       datalink_ChoosePhoneNumber(dl) : "");
27481358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
275565e35e5SBrian Somers                 dl->cfg.dial.max)
276dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
277c11e57a3SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial.tries,
278565e35e5SBrian Somers                         dl->cfg.dial.max);
279c5a5a6caSBrian Somers           } else
280c5a5a6caSBrian Somers             datalink_LoginDone(dl);
2818b09cf1cSBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
2823006ec67SBrian Somers         } else {
28381358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
284565e35e5SBrian Somers               dl->cfg.dial.max)
2855d9e6103SBrian Somers             log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
286c11e57a3SBrian Somers                        dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
2873006ec67SBrian Somers           else
2885d9e6103SBrian Somers             log_Printf(LogCHAT, "Failed to open device\n");
2893006ec67SBrian Somers 
2903b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
29181358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
292c11e57a3SBrian Somers                dl->cfg.dial.max && dl->dial.tries == 0)) {
2939ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
2943006ec67SBrian Somers             dl->reconnect_tries = 0;
295c11e57a3SBrian Somers             dl->dial.tries = -1;
296bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s\n",
297bf1d3ff6SBrian Somers                              dl->physical->name.full);
2985b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
299c5a5a6caSBrian Somers           }
300bf1d3ff6SBrian Somers           if (!dl->bundle->CleaningUp) {
301c11e57a3SBrian Somers             int timeout;
302c11e57a3SBrian Somers 
303c11e57a3SBrian Somers             timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
304bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
305b5c347a3SBrian Somers                              dl->physical->name.full, timeout);
3063006ec67SBrian Somers           }
3073006ec67SBrian Somers         }
308bf1d3ff6SBrian Somers       }
309310c3babSBrian Somers       break;
3103006ec67SBrian Somers 
311eb6e5e05SBrian Somers     case DATALINK_CARRIER:
312eb6e5e05SBrian Somers       /* Wait for carrier on the device */
313eb6e5e05SBrian Somers       switch (physical_AwaitCarrier(dl->physical)) {
314eb6e5e05SBrian Somers         case CARRIER_PENDING:
315eb6e5e05SBrian Somers           log_Printf(LogDEBUG, "Waiting for carrier\n");
316eb6e5e05SBrian Somers           return 0;	/* A device timer is running to wake us up again */
317eb6e5e05SBrian Somers 
318eb6e5e05SBrian Somers         case CARRIER_OK:
319eb6e5e05SBrian Somers           datalink_NewState(dl, DATALINK_LOGIN);
320eb6e5e05SBrian Somers           chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
321eb6e5e05SBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
322eb6e5e05SBrian Somers 
323eb6e5e05SBrian Somers         case CARRIER_LOST:
324eb6e5e05SBrian Somers           datalink_NewState(dl, DATALINK_HANGUP);
325eb6e5e05SBrian Somers           physical_Offline(dl->physical);	/* Is this required ? */
326eb6e5e05SBrian Somers           chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
327eb6e5e05SBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
328eb6e5e05SBrian Somers       }
329eb6e5e05SBrian Somers 
3303006ec67SBrian Somers     case DATALINK_HANGUP:
3313006ec67SBrian Somers     case DATALINK_DIAL:
3323006ec67SBrian Somers     case DATALINK_LOGIN:
3333006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
3343006ec67SBrian Somers       switch (dl->chat.state) {
3353006ec67SBrian Somers         case CHAT_DONE:
3363006ec67SBrian Somers           /* script succeeded */
33739d94652SBrian Somers           chat_Destroy(&dl->chat);
3383006ec67SBrian Somers           switch(dl->state) {
3393006ec67SBrian Somers             case DATALINK_HANGUP:
3403006ec67SBrian Somers               datalink_HangupDone(dl);
3413006ec67SBrian Somers               break;
3423006ec67SBrian Somers             case DATALINK_DIAL:
343eb6e5e05SBrian Somers               datalink_NewState(dl, DATALINK_CARRIER);
3441342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3453006ec67SBrian Somers             case DATALINK_LOGIN:
34647dd77c1SBrian Somers               dl->phone.alt = NULL;
347c5a5a6caSBrian Somers               datalink_LoginDone(dl);
348b51a60ccSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3493006ec67SBrian Somers           }
3503006ec67SBrian Somers           break;
3513006ec67SBrian Somers         case CHAT_FAILED:
3523006ec67SBrian Somers           /* Going down - script failed */
353dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
35439d94652SBrian Somers           chat_Destroy(&dl->chat);
3553006ec67SBrian Somers           switch(dl->state) {
3563006ec67SBrian Somers             case DATALINK_HANGUP:
3573006ec67SBrian Somers               datalink_HangupDone(dl);
3583006ec67SBrian Somers               break;
3593006ec67SBrian Somers             case DATALINK_DIAL:
3603006ec67SBrian Somers             case DATALINK_LOGIN:
3619ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
3625d9e6103SBrian Somers               physical_Offline(dl->physical);	/* Is this required ? */
363eb6e5e05SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup,
364eb6e5e05SBrian Somers                         1, NULL);
3651342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3663006ec67SBrian Somers           }
3673006ec67SBrian Somers           break;
3683006ec67SBrian Somers       }
3693006ec67SBrian Somers       break;
370c7cc5030SBrian Somers 
371c7cc5030SBrian Somers     case DATALINK_READY:
372e2ebb036SBrian Somers     case DATALINK_LCP:
373e2ebb036SBrian Somers     case DATALINK_AUTH:
37492b09558SBrian Somers     case DATALINK_CBCP:
3753006ec67SBrian Somers     case DATALINK_OPEN:
37658330d7bSBrian Somers       result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
37758330d7bSBrian Somers                descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3783006ec67SBrian Somers       break;
3793006ec67SBrian Somers   }
3803006ec67SBrian Somers   return result;
3813006ec67SBrian Somers }
3823006ec67SBrian Somers 
383ea722969SBrian Somers int
384ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
385ea722969SBrian Somers {
386ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
387ea722969SBrian Somers }
388ea722969SBrian Somers 
3893006ec67SBrian Somers static int
3902f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3913006ec67SBrian Somers {
3923006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3933006ec67SBrian Somers 
3943006ec67SBrian Somers   switch (dl->state) {
3953006ec67SBrian Somers     case DATALINK_CLOSED:
396c7cc5030SBrian Somers     case DATALINK_OPENING:
3973006ec67SBrian Somers       break;
398c7cc5030SBrian Somers 
3993006ec67SBrian Somers     case DATALINK_HANGUP:
4003006ec67SBrian Somers     case DATALINK_DIAL:
4013006ec67SBrian Somers     case DATALINK_LOGIN:
4023006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
403c7cc5030SBrian Somers 
404c7cc5030SBrian Somers     case DATALINK_READY:
405e2ebb036SBrian Somers     case DATALINK_LCP:
406e2ebb036SBrian Somers     case DATALINK_AUTH:
40792b09558SBrian Somers     case DATALINK_CBCP:
4083006ec67SBrian Somers     case DATALINK_OPEN:
40958330d7bSBrian Somers       return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
41058330d7bSBrian Somers              descriptor_IsSet(&dl->physical->desc, fdset);
4113006ec67SBrian Somers   }
4123006ec67SBrian Somers   return 0;
4133006ec67SBrian Somers }
4143006ec67SBrian Somers 
4153006ec67SBrian Somers static void
4163006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
4173006ec67SBrian Somers {
4183006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4193006ec67SBrian Somers 
4203006ec67SBrian Somers   switch (dl->state) {
4213006ec67SBrian Somers     case DATALINK_CLOSED:
422c7cc5030SBrian Somers     case DATALINK_OPENING:
4233006ec67SBrian Somers       break;
424c7cc5030SBrian Somers 
4253006ec67SBrian Somers     case DATALINK_HANGUP:
4263006ec67SBrian Somers     case DATALINK_DIAL:
4273006ec67SBrian Somers     case DATALINK_LOGIN:
4283006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
4293006ec67SBrian Somers       break;
430c7cc5030SBrian Somers 
431c7cc5030SBrian Somers     case DATALINK_READY:
432e2ebb036SBrian Somers     case DATALINK_LCP:
433e2ebb036SBrian Somers     case DATALINK_AUTH:
43492b09558SBrian Somers     case DATALINK_CBCP:
4353006ec67SBrian Somers     case DATALINK_OPEN:
43658330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
43758330d7bSBrian Somers         descriptor_Read(&dl->chap.desc, bundle, fdset);
43858330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
4393006ec67SBrian Somers         descriptor_Read(&dl->physical->desc, bundle, fdset);
4403006ec67SBrian Somers       break;
4413006ec67SBrian Somers   }
4423006ec67SBrian Somers }
4433006ec67SBrian Somers 
4441af29a6eSBrian Somers static int
445f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
4463006ec67SBrian Somers {
4473006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4481af29a6eSBrian Somers   int result = 0;
4493006ec67SBrian Somers 
4503006ec67SBrian Somers   switch (dl->state) {
4513006ec67SBrian Somers     case DATALINK_CLOSED:
452c7cc5030SBrian Somers     case DATALINK_OPENING:
4533006ec67SBrian Somers       break;
454c7cc5030SBrian Somers 
4553006ec67SBrian Somers     case DATALINK_HANGUP:
4563006ec67SBrian Somers     case DATALINK_DIAL:
4573006ec67SBrian Somers     case DATALINK_LOGIN:
4581af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4593006ec67SBrian Somers       break;
460c7cc5030SBrian Somers 
461c7cc5030SBrian Somers     case DATALINK_READY:
462e2ebb036SBrian Somers     case DATALINK_LCP:
463e2ebb036SBrian Somers     case DATALINK_AUTH:
46492b09558SBrian Somers     case DATALINK_CBCP:
4653006ec67SBrian Somers     case DATALINK_OPEN:
46658330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
46758330d7bSBrian Somers         result += descriptor_Write(&dl->chap.desc, bundle, fdset);
46858330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
46958330d7bSBrian Somers         result += descriptor_Write(&dl->physical->desc, bundle, fdset);
4703006ec67SBrian Somers       break;
4713006ec67SBrian Somers   }
4721af29a6eSBrian Somers 
4731af29a6eSBrian Somers   return result;
4743006ec67SBrian Somers }
4753006ec67SBrian Somers 
4766d666775SBrian Somers static void
4779c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
478e2ebb036SBrian Somers {
4799c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
480c11e57a3SBrian Somers     dl->dial.tries = -1;
481e2ebb036SBrian Somers     dl->reconnect_tries = 0;
4827729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4839c81b87dSBrian Somers       dl->stayonline = 1;
484e2ebb036SBrian Somers   }
485e2ebb036SBrian Somers 
4867729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4879c81b87dSBrian Somers     dl->stayonline = 0;
4886815097bSBrian Somers     physical_StopDeviceTimer(dl->physical);
4899c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4909c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
4915d9e6103SBrian Somers     physical_Offline(dl->physical);
492b6f5f442SBrian Somers     chat_Destroy(&dl->chat);
493e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4949ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
495f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
496e2ebb036SBrian Somers     } else
497e2ebb036SBrian Somers       datalink_HangupDone(dl);
498e2ebb036SBrian Somers   }
499e2ebb036SBrian Somers }
500e2ebb036SBrian Somers 
501e2ebb036SBrian Somers static void
5026d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
5036d666775SBrian Somers {
5046d666775SBrian Somers   /* The given FSM is about to start up ! */
5056d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
506a611cad6SBrian Somers 
50792e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
508e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
509e2ebb036SBrian Somers }
5106d666775SBrian Somers 
5116d666775SBrian Somers static void
5126d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
5136d666775SBrian Somers {
5146d666775SBrian Somers   /* The given fsm is now up */
5156d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
5165e315498SBrian Somers   struct lcp *lcp = &dl->physical->link.lcp;
5176d666775SBrian Somers 
51892e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
519f0cdd9c0SBrian Somers     datalink_GotAuthname(dl, "");
5205e315498SBrian Somers     lcp->auth_ineed = lcp->want_auth;
5215e315498SBrian Somers     lcp->auth_iwait = lcp->his_auth;
5225e315498SBrian Somers     if (lcp->his_auth || lcp->want_auth) {
5235945a079SBrian Somers       if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
5245563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
525dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
5265e315498SBrian Somers                 Auth2Nam(lcp->his_auth, lcp->his_authtype),
5275e315498SBrian Somers                 Auth2Nam(lcp->want_auth, lcp->want_authtype));
5285e315498SBrian Somers       if (lcp->his_auth == PROTO_PAP)
529f0cdd9c0SBrian Somers         auth_StartReq(&dl->pap);
5305e315498SBrian Somers       if (lcp->want_auth == PROTO_CHAP)
531f0cdd9c0SBrian Somers         auth_StartReq(&dl->chap.auth);
532e2ebb036SBrian Somers     } else
533e2ebb036SBrian Somers       datalink_AuthOk(dl);
534e2ebb036SBrian Somers   }
535e2ebb036SBrian Somers }
536e2ebb036SBrian Somers 
53764cfdfc6SBrian Somers static void
53864cfdfc6SBrian Somers datalink_AuthReInit(struct datalink *dl)
53964cfdfc6SBrian Somers {
54064cfdfc6SBrian Somers   auth_StopTimer(&dl->pap);
54164cfdfc6SBrian Somers   auth_StopTimer(&dl->chap.auth);
54264cfdfc6SBrian Somers   chap_ReInit(&dl->chap);
54364cfdfc6SBrian Somers }
54464cfdfc6SBrian Somers 
545e2ebb036SBrian Somers void
546f0cdd9c0SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name)
547643f4904SBrian Somers {
548f0cdd9c0SBrian Somers   strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
549f0cdd9c0SBrian Somers   dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
550643f4904SBrian Somers }
551643f4904SBrian Somers 
552643f4904SBrian Somers void
55392b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
554e2ebb036SBrian Somers {
55506337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5561df0a3b9SBrian Somers 
557dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5581fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5591fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5601fa665f5SBrian Somers       case MP_LINKSENT:
561ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5621fa665f5SBrian Somers         return;
5631fa665f5SBrian Somers       case MP_UP:
5640a1b5c9dSBrian Somers         /* First link in the bundle */
56592b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
566ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
5670a1b5c9dSBrian Somers         /* fall through */
5681fa665f5SBrian Somers       case MP_ADDED:
5690a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5701df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
571ab2de065SBrian Somers         bundle_CalculateBandwidth(dl->bundle);
5721fa665f5SBrian Somers         break;
5731fa665f5SBrian Somers       case MP_FAILED:
57449052c95SBrian Somers         datalink_AuthNotOk(dl);
57549052c95SBrian Somers         return;
57649052c95SBrian Somers     }
57749052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
578dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
579897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
580ab2de065SBrian Somers     bundle_CalculateBandwidth(dl->bundle);
581897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
58249052c95SBrian Somers     return;
58350abd4c8SBrian Somers   } else {
58450abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
585ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
58692b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
58750abd4c8SBrian Somers   }
58849052c95SBrian Somers 
58906337856SBrian Somers   if (ccpok) {
590dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
591dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
59206337856SBrian Somers   }
5939ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5943b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5953b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5966d666775SBrian Somers }
597e2ebb036SBrian Somers 
598e2ebb036SBrian Somers void
59992b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
60092b09558SBrian Somers {
60192b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
60264cfdfc6SBrian Somers   datalink_AuthReInit(dl);
60392b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
60492b09558SBrian Somers }
60592b09558SBrian Somers 
60692b09558SBrian Somers void
60792b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
60892b09558SBrian Somers {
60992b09558SBrian Somers   cbcp_Down(&dl->cbcp);
61092b09558SBrian Somers   datalink_CBCPComplete(dl);
61192b09558SBrian Somers }
61292b09558SBrian Somers 
61392b09558SBrian Somers void
61492b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
61592b09558SBrian Somers {
6165165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
61792b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
6185165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
6195165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
6205165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
6215165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
6225165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
62392b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
62492b09558SBrian Somers     cbcp_Up(&dl->cbcp);
62592b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
6265165af6fSBrian Somers     /* It's not CBCP */
62792b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
62892b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
62964cfdfc6SBrian Somers     datalink_AuthReInit(dl);
63092b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
63192b09558SBrian Somers   } else
63292b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
63392b09558SBrian Somers       case 0:
63492b09558SBrian Somers         datalink_NCPUp(dl);
63592b09558SBrian Somers         break;
63692b09558SBrian Somers 
63792b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
63892b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
63992b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
64092b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
64192b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
64292b09558SBrian Somers                      dl->peer.authname);
64392b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
64492b09558SBrian Somers         } else {
64592b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
64692b09558SBrian Somers           if (ptr)
64792b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
64892b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
64992b09558SBrian Somers                      dl->cbcp.fsm.phone);
65092b09558SBrian Somers           dl->cbcp.required = 1;
65192b09558SBrian Somers         }
65292b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
65392b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
65464cfdfc6SBrian Somers         datalink_AuthReInit(dl);
65592b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
65692b09558SBrian Somers         break;
65792b09558SBrian Somers 
65892b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
65992b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
66092b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
66192b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
66292b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
66392b09558SBrian Somers                    dl->cbcp.fsm.phone);
66492b09558SBrian Somers         dl->cbcp.required = 1;
66592b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
66692b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
66764cfdfc6SBrian Somers         datalink_AuthReInit(dl);
66892b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
66992b09558SBrian Somers         break;
67092b09558SBrian Somers 
67192b09558SBrian Somers       default:
67292b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
67392b09558SBrian Somers                    dl->name);
67492b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
67564cfdfc6SBrian Somers         datalink_AuthReInit(dl);
67692b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
67792b09558SBrian Somers         break;
67892b09558SBrian Somers     }
67992b09558SBrian Somers }
68092b09558SBrian Somers 
68192b09558SBrian Somers void
682e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
683e2ebb036SBrian Somers {
6849ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
68564cfdfc6SBrian Somers   datalink_AuthReInit(dl);
686dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6876d666775SBrian Somers }
6886d666775SBrian Somers 
6896d666775SBrian Somers static void
6906d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6916d666775SBrian Somers {
6926d666775SBrian Somers   /* The given FSM has been told to come down */
6936d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
694a611cad6SBrian Somers 
69592e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
696e2ebb036SBrian Somers     switch (dl->state) {
697e2ebb036SBrian Somers       case DATALINK_OPEN:
698643f4904SBrian Somers         peerid_Init(&dl->peer);
69909206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
700ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
701e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
70292b09558SBrian Somers         /* fall through (just in case) */
70392b09558SBrian Somers 
70492b09558SBrian Somers       case DATALINK_CBCP:
70592b09558SBrian Somers         if (!dl->cbcp.required)
70692b09558SBrian Somers           cbcp_Down(&dl->cbcp);
70792b09558SBrian Somers         /* fall through (just in case) */
708e2ebb036SBrian Somers 
709e2ebb036SBrian Somers       case DATALINK_AUTH:
710dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
711dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
7126d666775SBrian Somers     }
7139ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
71464cfdfc6SBrian Somers     datalink_AuthReInit(dl);
715e2ebb036SBrian Somers   }
7166d666775SBrian Somers }
7176d666775SBrian Somers 
7186d666775SBrian Somers static void
7196d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
7206d666775SBrian Somers {
7216d666775SBrian Somers   /* The given fsm is now down */
7226d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
7236d666775SBrian Somers 
72492e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
72509206a6fSBrian Somers     fsm2initial(fp);
7266d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
7279c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
7280a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
7290a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
7306d666775SBrian Somers }
7316d666775SBrian Somers 
7323006ec67SBrian Somers struct datalink *
7336f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
7343006ec67SBrian Somers {
7353006ec67SBrian Somers   struct datalink *dl;
7363006ec67SBrian Somers 
7373006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
7383006ec67SBrian Somers   if (dl == NULL)
7393006ec67SBrian Somers     return dl;
7403006ec67SBrian Somers 
7413006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
7423006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
7433006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
7443006ec67SBrian Somers   dl->desc.Read = datalink_Read;
7453006ec67SBrian Somers   dl->desc.Write = datalink_Write;
7465b8b8060SBrian Somers 
747e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
748e718d1d7SBrian Somers 
74973a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
75073a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
75173a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
752f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
753f9545805SBrian Somers   *dl->phone.list = '\0';
754f9545805SBrian Somers   dl->phone.next = NULL;
755f9545805SBrian Somers   dl->phone.alt = NULL;
756f9545805SBrian Somers   dl->phone.chosen = "N/A";
7579c81b87dSBrian Somers   dl->stayonline = 0;
758c7cc5030SBrian Somers   dl->script.run = 1;
759c7cc5030SBrian Somers   dl->script.packetmode = 1;
7603b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
7615b8b8060SBrian Somers 
7623006ec67SBrian Somers   dl->bundle = bundle;
7633006ec67SBrian Somers   dl->next = NULL;
764e718d1d7SBrian Somers 
765c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
766e718d1d7SBrian Somers 
767c11e57a3SBrian Somers   dl->dial.tries = 0;
768565e35e5SBrian Somers   dl->cfg.dial.max = 1;
769565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
770565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
771c11e57a3SBrian Somers   dl->cfg.dial.inc = 0;
772c11e57a3SBrian Somers   dl->cfg.dial.maxinc = 10;
773e718d1d7SBrian Somers 
774abff9baeSBrian Somers   dl->reconnect_tries = 0;
775565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
776565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
777abff9baeSBrian Somers 
77892b09558SBrian Somers   dl->cfg.callback.opmask = 0;
77992b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
78092b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
78192b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
78292b09558SBrian Somers 
7833006ec67SBrian Somers   dl->name = strdup(name);
784643f4904SBrian Somers   peerid_Init(&dl->peer);
7856f384573SBrian Somers   dl->parent = &bundle->fsm;
7863b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7873b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7883b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7893b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7903b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7913b0f8d2eSBrian Somers 
7925d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, type)) == NULL) {
7933006ec67SBrian Somers     free(dl->name);
7943006ec67SBrian Somers     free(dl);
7953006ec67SBrian Somers     return NULL;
7963006ec67SBrian Somers   }
797f0cdd9c0SBrian Somers 
798f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
799f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
80092b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
801f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
8023006ec67SBrian Somers 
8039ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
8049ae58882SBrian Somers              dl->name, datalink_State(dl));
8053006ec67SBrian Somers 
8063006ec67SBrian Somers   return dl;
8073006ec67SBrian Somers }
8083006ec67SBrian Somers 
8093006ec67SBrian Somers struct datalink *
810cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
811cd7bd93aSBrian Somers {
812cd7bd93aSBrian Somers   struct datalink *dl;
813cd7bd93aSBrian Somers 
814cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
815cd7bd93aSBrian Somers   if (dl == NULL)
816cd7bd93aSBrian Somers     return dl;
817cd7bd93aSBrian Somers 
818cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
819cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
820cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
821cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
822cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
823cd7bd93aSBrian Somers 
824cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
825cd7bd93aSBrian Somers 
826cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
827cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
828cd7bd93aSBrian Somers   *dl->phone.list = '\0';
829643f4904SBrian Somers   dl->phone.next = NULL;
830643f4904SBrian Somers   dl->phone.alt = NULL;
831643f4904SBrian Somers   dl->phone.chosen = "N/A";
832cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
833cd7bd93aSBrian Somers   dl->next = NULL;
834c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
835c11e57a3SBrian Somers   dl->dial.tries = 0;
836cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
837cd7bd93aSBrian Somers   dl->name = strdup(name);
838643f4904SBrian Somers   peerid_Init(&dl->peer);
839cd7bd93aSBrian Somers   dl->parent = odl->parent;
840cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
841643f4904SBrian Somers   dl->fsmp.object = dl;
842cd7bd93aSBrian Somers 
8435d9e6103SBrian Somers   if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
844cd7bd93aSBrian Somers     free(dl->name);
845cd7bd93aSBrian Somers     free(dl);
846cd7bd93aSBrian Somers     return NULL;
847cd7bd93aSBrian Somers   }
848f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
849479508cfSBrian Somers   dl->pap.cfg = odl->pap.cfg;
850f0cdd9c0SBrian Somers 
851f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
852479508cfSBrian Somers   dl->chap.auth.cfg = odl->chap.auth.cfg;
853f0cdd9c0SBrian Somers 
854cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
855cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
856cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
857cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
858cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
859cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
860cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
861cd7bd93aSBrian Somers 
86292b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
863cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
864cd7bd93aSBrian Somers 
8659ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
8669ae58882SBrian Somers              dl->name, datalink_State(dl));
867cd7bd93aSBrian Somers 
868cd7bd93aSBrian Somers   return dl;
869cd7bd93aSBrian Somers }
870cd7bd93aSBrian Somers 
871cd7bd93aSBrian Somers struct datalink *
8723006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
8733006ec67SBrian Somers {
8743006ec67SBrian Somers   struct datalink *result;
8753006ec67SBrian Somers 
87639d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
877dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
878c7cc5030SBrian Somers               datalink_State(dl));
87939d94652SBrian Somers     switch (dl->state) {
88039d94652SBrian Somers       case DATALINK_HANGUP:
88139d94652SBrian Somers       case DATALINK_DIAL:
88239d94652SBrian Somers       case DATALINK_LOGIN:
88339d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
88439d94652SBrian Somers         break;
88539d94652SBrian Somers     }
88639d94652SBrian Somers   }
8873006ec67SBrian Somers 
888c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
8893006ec67SBrian Somers   result = dl->next;
8905d9e6103SBrian Somers   physical_Destroy(dl->physical);
8913006ec67SBrian Somers   free(dl->name);
8923006ec67SBrian Somers   free(dl);
8933006ec67SBrian Somers 
8943006ec67SBrian Somers   return result;
8953006ec67SBrian Somers }
8963006ec67SBrian Somers 
8973006ec67SBrian Somers void
898c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8993006ec67SBrian Somers {
9006f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
901565e35e5SBrian Somers     /* Ignore scripts */
902565e35e5SBrian Somers     runscripts = 0;
903565e35e5SBrian Somers 
904c7cc5030SBrian Somers   switch (dl->state) {
905c7cc5030SBrian Somers     case DATALINK_CLOSED:
9063b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
9073b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
9083b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
9099ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
910565e35e5SBrian Somers       dl->reconnect_tries =
9116f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
912c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
913c5a5a6caSBrian Somers       dl->script.run = runscripts;
914c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
915c7cc5030SBrian Somers       break;
916c7cc5030SBrian Somers 
917c7cc5030SBrian Somers     case DATALINK_OPENING:
918c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
919c7cc5030SBrian Somers         dl->script.run = 1;
920c7cc5030SBrian Somers       /* fall through */
921c7cc5030SBrian Somers 
922c7cc5030SBrian Somers     case DATALINK_DIAL:
923c7cc5030SBrian Somers     case DATALINK_LOGIN:
924c7cc5030SBrian Somers     case DATALINK_READY:
925c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
926c7cc5030SBrian Somers         dl->script.packetmode = 1;
927c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
928c7cc5030SBrian Somers           datalink_LoginDone(dl);
929c7cc5030SBrian Somers       }
930c7cc5030SBrian Somers       break;
9313006ec67SBrian Somers   }
9323006ec67SBrian Somers }
9333006ec67SBrian Somers 
9343006ec67SBrian Somers void
9359c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
936c7cc5030SBrian Somers {
937c7cc5030SBrian Somers   /* Please close */
938e2ebb036SBrian Somers   switch (dl->state) {
939e2ebb036SBrian Somers     case DATALINK_OPEN:
940643f4904SBrian Somers       peerid_Init(&dl->peer);
94109206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
942e2ebb036SBrian Somers       /* fall through */
943e2ebb036SBrian Somers 
94492b09558SBrian Somers     case DATALINK_CBCP:
945e2ebb036SBrian Somers     case DATALINK_AUTH:
946e2ebb036SBrian Somers     case DATALINK_LCP:
94764cfdfc6SBrian Somers       datalink_AuthReInit(dl);
948dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
9499c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
950c11e57a3SBrian Somers         dl->dial.tries = -1;
951c7cc5030SBrian Somers         dl->reconnect_tries = 0;
9529c81b87dSBrian Somers         if (how == CLOSE_LCP)
9539c81b87dSBrian Somers           dl->stayonline = 1;
954c7cc5030SBrian Somers       }
955e2ebb036SBrian Somers       break;
956e2ebb036SBrian Somers 
957e2ebb036SBrian Somers     default:
9589c81b87dSBrian Somers       datalink_ComeDown(dl, how);
959c7cc5030SBrian Somers   }
960e2ebb036SBrian Somers }
961c7cc5030SBrian Somers 
962c7cc5030SBrian Somers void
9639c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
964c7cc5030SBrian Somers {
965c7cc5030SBrian Somers   /* Carrier is lost */
966e2ebb036SBrian Somers   switch (dl->state) {
967e2ebb036SBrian Somers     case DATALINK_OPEN:
968643f4904SBrian Somers       peerid_Init(&dl->peer);
96909206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
970e2ebb036SBrian Somers       /* fall through */
971e2ebb036SBrian Somers 
97292b09558SBrian Somers     case DATALINK_CBCP:
973e2ebb036SBrian Somers     case DATALINK_AUTH:
974e2ebb036SBrian Somers     case DATALINK_LCP:
97509206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
976e2ebb036SBrian Somers       /* fall through */
977c7cc5030SBrian Somers 
978e2ebb036SBrian Somers     default:
9799c81b87dSBrian Somers       datalink_ComeDown(dl, how);
980c7cc5030SBrian Somers   }
981e2ebb036SBrian Somers }
982c7cc5030SBrian Somers 
983c7cc5030SBrian Somers void
9843006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9853006ec67SBrian Somers {
9863006ec67SBrian Somers   dl->reconnect_tries = 0;
9873006ec67SBrian Somers }
988c7cc5030SBrian Somers 
9899c81b87dSBrian Somers void
9909c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9919c81b87dSBrian Somers {
9927729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9939c81b87dSBrian Somers     dl->stayonline = 1;
9949c81b87dSBrian Somers }
9959c81b87dSBrian Somers 
996643f4904SBrian Somers int
997643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
998c7cc5030SBrian Somers {
999643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
1000643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
1001643f4904SBrian Somers                 datalink_State(arg->cx));
1002643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
1003643f4904SBrian Somers   if (*arg->cx->peer.authname)
1004643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
1005643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
1006643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
1007565e35e5SBrian Somers   else
1008643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
1009643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
1010643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
1011643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
1012643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
1013643f4904SBrian Somers 
1014643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
1015643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
1016643f4904SBrian Somers                 arg->cx->cfg.phone.list);
1017643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
1018643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
1019643f4904SBrian Somers                   arg->cx->cfg.dial.max);
1020565e35e5SBrian Somers   else
1021643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
1022b5c347a3SBrian Somers   if (arg->cx->cfg.dial.next_timeout >= 0)
1023643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
1024565e35e5SBrian Somers   else
1025643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
1026b5c347a3SBrian Somers   if (arg->cx->cfg.dial.timeout >= 0)
1027643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
1028565e35e5SBrian Somers   else
1029643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
1030643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
1031643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
1032643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
1033643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
1034643f4904SBrian Somers   else
1035643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
103692b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
103792b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
103892b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
103992b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
104092b09558SBrian Somers   else {
104192b09558SBrian Somers     int comma = 0;
104292b09558SBrian Somers 
104392b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
104492b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
104592b09558SBrian Somers       comma = 1;
104692b09558SBrian Somers     }
104792b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
104892b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
104992b09558SBrian Somers       comma = 1;
105092b09558SBrian Somers     }
105192b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
105292b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
105392b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
105492b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
105592b09558SBrian Somers       comma = 1;
105692b09558SBrian Somers     }
105792b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
105892b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
105992b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
106092b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
1061cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
1062cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
1063cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
1064cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
1065cf784a89SBrian Somers         else
1066cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
1067cf784a89SBrian Somers       } else
1068cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
106992b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
107092b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
107192b09558SBrian Somers     } else
107292b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
107392b09558SBrian Somers   }
107492b09558SBrian Somers 
1075643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1076643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1077643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1078643f4904SBrian Somers                 arg->cx->cfg.script.login);
1079643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1080643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1081643f4904SBrian Somers   return 0;
1082565e35e5SBrian Somers }
1083565e35e5SBrian Somers 
1084565e35e5SBrian Somers int
1085565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1086565e35e5SBrian Somers {
108725092092SBrian Somers   if (arg->argc == arg->argn+2) {
108825092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
108925092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1090565e35e5SBrian Somers     return 0;
1091565e35e5SBrian Somers   }
1092565e35e5SBrian Somers   return -1;
1093565e35e5SBrian Somers }
1094565e35e5SBrian Somers 
1095565e35e5SBrian Somers int
1096565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1097565e35e5SBrian Somers {
1098c11e57a3SBrian Somers   const char *sep, *osep;
1099c11e57a3SBrian Somers   int timeout, inc, maxinc, tries;
1100565e35e5SBrian Somers 
110125092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
110225092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
110325092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1104565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1105565e35e5SBrian Somers       randinit();
1106565e35e5SBrian Somers     } else {
110725092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1108565e35e5SBrian Somers 
1109565e35e5SBrian Somers       if (timeout >= 0)
1110565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1111565e35e5SBrian Somers       else {
1112dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1113565e35e5SBrian Somers 	return -1;
1114565e35e5SBrian Somers       }
1115565e35e5SBrian Somers     }
1116565e35e5SBrian Somers 
1117c11e57a3SBrian Somers     sep = strchr(arg->argv[arg->argn], '+');
1118c11e57a3SBrian Somers     if (sep) {
1119c11e57a3SBrian Somers       inc = atoi(++sep);
1120c11e57a3SBrian Somers       osep = sep;
1121c11e57a3SBrian Somers       if (inc >= 0)
1122c11e57a3SBrian Somers         arg->cx->cfg.dial.inc = inc;
1123c11e57a3SBrian Somers       else {
1124c11e57a3SBrian Somers         log_Printf(LogWARN, "Invalid timeout increment\n");
1125c11e57a3SBrian Somers         return -1;
1126c11e57a3SBrian Somers       }
1127c11e57a3SBrian Somers       sep = strchr(sep, '-');
1128c11e57a3SBrian Somers       if (sep) {
1129c11e57a3SBrian Somers         maxinc = atoi(++sep);
1130c11e57a3SBrian Somers         if (maxinc >= 0)
1131c11e57a3SBrian Somers           arg->cx->cfg.dial.maxinc = maxinc;
1132c11e57a3SBrian Somers         else {
1133c11e57a3SBrian Somers           log_Printf(LogWARN, "Invalid maximum timeout increments\n");
1134c11e57a3SBrian Somers           return -1;
1135c11e57a3SBrian Somers         }
1136c11e57a3SBrian Somers       } else {
1137c11e57a3SBrian Somers         /* Default timeout increment */
1138c11e57a3SBrian Somers         arg->cx->cfg.dial.maxinc = 10;
1139c11e57a3SBrian Somers         sep = osep;
1140c11e57a3SBrian Somers       }
1141c11e57a3SBrian Somers     } else {
1142c11e57a3SBrian Somers       /* Default timeout increment & max increment */
1143c11e57a3SBrian Somers       arg->cx->cfg.dial.inc = 0;
1144c11e57a3SBrian Somers       arg->cx->cfg.dial.maxinc = 10;
1145c11e57a3SBrian Somers       sep = arg->argv[arg->argn];
1146c11e57a3SBrian Somers     }
1147c11e57a3SBrian Somers 
1148c11e57a3SBrian Somers     sep = strchr(sep, '.');
1149c11e57a3SBrian Somers     if (sep) {
1150c11e57a3SBrian Somers       if (strcasecmp(++sep, "random") == 0) {
1151565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1152565e35e5SBrian Somers 	randinit();
1153565e35e5SBrian Somers       } else {
1154c11e57a3SBrian Somers 	timeout = atoi(sep);
1155565e35e5SBrian Somers 	if (timeout >= 0)
1156565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1157565e35e5SBrian Somers 	else {
1158dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1159565e35e5SBrian Somers 	  return -1;
1160565e35e5SBrian Somers 	}
1161565e35e5SBrian Somers       }
1162565e35e5SBrian Somers     } else
1163565e35e5SBrian Somers       /* Default next timeout */
1164565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1165565e35e5SBrian Somers 
116625092092SBrian Somers     if (arg->argc == arg->argn+2) {
116725092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1168565e35e5SBrian Somers 
1169565e35e5SBrian Somers       if (tries >= 0) {
1170565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1171565e35e5SBrian Somers       } else {
1172dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1173565e35e5SBrian Somers 	return 1;
1174565e35e5SBrian Somers       }
1175565e35e5SBrian Somers     }
1176565e35e5SBrian Somers     return 0;
1177565e35e5SBrian Somers   }
1178c11e57a3SBrian Somers 
1179565e35e5SBrian Somers   return -1;
1180c7cc5030SBrian Somers }
1181c7cc5030SBrian Somers 
1182cdbbb6b5SBrian Somers static const char *states[] = {
1183565e35e5SBrian Somers   "closed",
1184565e35e5SBrian Somers   "opening",
1185565e35e5SBrian Somers   "hangup",
1186565e35e5SBrian Somers   "dial",
1187eb6e5e05SBrian Somers   "carrier",
1188565e35e5SBrian Somers   "login",
1189565e35e5SBrian Somers   "ready",
1190565e35e5SBrian Somers   "lcp",
1191565e35e5SBrian Somers   "auth",
119292b09558SBrian Somers   "cbcp",
1193565e35e5SBrian Somers   "open"
1194c7cc5030SBrian Somers };
1195c7cc5030SBrian Somers 
1196643f4904SBrian Somers const char *
1197c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1198c7cc5030SBrian Somers {
1199c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1200c7cc5030SBrian Somers     return "unknown";
1201c7cc5030SBrian Somers   return states[dl->state];
1202c7cc5030SBrian Somers }
12036f384573SBrian Somers 
12049ae58882SBrian Somers static void
12059ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
12069ae58882SBrian Somers {
12079ae58882SBrian Somers   if (state != dl->state) {
12089ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
12099ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
12109ae58882SBrian Somers                  states[state]);
12119ae58882SBrian Somers       dl->state = state;
12129ae58882SBrian Somers     } else
12139ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
12149ae58882SBrian Somers   }
12159ae58882SBrian Somers }
12169ae58882SBrian Somers 
12176f384573SBrian Somers struct datalink *
121896c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
121996c9bb21SBrian Somers              int fd)
12206f384573SBrian Somers {
1221b7c5748eSBrian Somers   struct datalink *dl, *cdl;
1222479508cfSBrian Somers   struct fsm_retry copy;
1223b7c5748eSBrian Somers   char *oname;
12246f384573SBrian Somers 
122596c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
122696c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
12276f384573SBrian Somers 
122896c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
122996c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
123096c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
123196c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
12326f384573SBrian Somers   }
1233b7c5748eSBrian Somers 
1234b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1235b7c5748eSBrian Somers   oname = NULL;
1236b7c5748eSBrian Somers   do {
1237b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1238b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1239b7c5748eSBrian Somers         if (oname)
1240b7c5748eSBrian Somers           free(datalink_NextName(dl));
1241b7c5748eSBrian Somers         else
1242b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1243b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1244b7c5748eSBrian Somers       }
1245b7c5748eSBrian Somers   } while (cdl);
1246b7c5748eSBrian Somers 
1247b7c5748eSBrian Somers   if (oname) {
1248b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1249b7c5748eSBrian Somers     free(oname);
1250b7c5748eSBrian Somers   } else {
125196c9bb21SBrian Somers     dl->name = strdup(dl->name);
1252b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1253b7c5748eSBrian Somers   }
1254b7c5748eSBrian Somers   (*niov)++;
12556f384573SBrian Somers 
12566f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
12576f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
12586f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
12596f384573SBrian Somers   dl->desc.Read = datalink_Read;
12606f384573SBrian Somers   dl->desc.Write = datalink_Write;
12616f384573SBrian Somers 
12626f384573SBrian Somers   mp_linkInit(&dl->mp);
12636f384573SBrian Somers   *dl->phone.list = '\0';
12646f384573SBrian Somers   dl->phone.next = NULL;
12656f384573SBrian Somers   dl->phone.alt = NULL;
12666f384573SBrian Somers   dl->phone.chosen = "N/A";
12676f384573SBrian Somers 
12686f384573SBrian Somers   dl->bundle = bundle;
12696f384573SBrian Somers   dl->next = NULL;
1270c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
1271c11e57a3SBrian Somers   dl->dial.tries = 0;
12726f384573SBrian Somers   dl->reconnect_tries = 0;
12736f384573SBrian Somers   dl->parent = &bundle->fsm;
12746f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
12756f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
12766f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
12776f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
12786f384573SBrian Somers   dl->fsmp.object = dl;
12796f384573SBrian Somers 
12805d9e6103SBrian Somers   dl->physical = iov2physical(dl, iov, niov, maxiov, fd);
128196c9bb21SBrian Somers 
128296c9bb21SBrian Somers   if (!dl->physical) {
12836f384573SBrian Somers     free(dl->name);
12846f384573SBrian Somers     free(dl);
12856f384573SBrian Somers     dl = NULL;
12869ae58882SBrian Somers   } else {
1287479508cfSBrian Somers     copy = dl->pap.cfg.fsm;
1288f0cdd9c0SBrian Somers     pap_Init(&dl->pap, dl->physical);
1289479508cfSBrian Somers     dl->pap.cfg.fsm = copy;
1290f0cdd9c0SBrian Somers 
1291479508cfSBrian Somers     copy = dl->chap.auth.cfg.fsm;
1292f0cdd9c0SBrian Somers     chap_Init(&dl->chap, dl->physical);
1293479508cfSBrian Somers     dl->chap.auth.cfg.fsm = copy;
1294f0cdd9c0SBrian Somers 
129592b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
12966f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
12976f384573SBrian Somers 
12989ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
12999ae58882SBrian Somers               dl->name, datalink_State(dl));
13009ae58882SBrian Somers   }
13019ae58882SBrian Somers 
13026f384573SBrian Somers   return dl;
13036f384573SBrian Somers }
13046f384573SBrian Somers 
13056f384573SBrian Somers int
130685fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
130785fd273aSBrian Somers              pid_t newpid)
13086f384573SBrian Somers {
130996c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
131096c9bb21SBrian Somers   int link_fd;
13116f384573SBrian Somers 
131296c9bb21SBrian Somers   if (dl) {
1313c11e57a3SBrian Somers     timer_Stop(&dl->dial.timer);
131492b09558SBrian Somers     /* The following is purely for the sake of paranoia */
131592b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1316dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1317dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
13186f384573SBrian Somers   }
13196f384573SBrian Somers 
132096c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
132196c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
132296c9bb21SBrian Somers     if (dl) {
13236f384573SBrian Somers       free(dl->name);
13246f384573SBrian Somers       free(dl);
132596c9bb21SBrian Somers     }
132696c9bb21SBrian Somers     return -1;
132796c9bb21SBrian Somers   }
132896c9bb21SBrian Somers 
132996c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
133096c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
133196c9bb21SBrian Somers   iov[*niov].iov_base =
133296c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
133396c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
133496c9bb21SBrian Somers 
13355d9e6103SBrian Somers   link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
133696c9bb21SBrian Somers 
133796c9bb21SBrian Somers   if (link_fd == -1 && dl) {
133896c9bb21SBrian Somers     free(dl->name);
133996c9bb21SBrian Somers     free(dl);
134096c9bb21SBrian Somers   }
13416f384573SBrian Somers 
13426f384573SBrian Somers   return link_fd;
13436f384573SBrian Somers }
13446f384573SBrian Somers 
134558d55334SBrian Somers void
134658d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
134758d55334SBrian Somers {
134858d55334SBrian Somers   free(dl->name);
134958d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
135058d55334SBrian Somers }
135158d55334SBrian Somers 
135284917b87SBrian Somers char *
135384917b87SBrian Somers datalink_NextName(struct datalink *dl)
13546f384573SBrian Somers {
13556f384573SBrian Somers   int f, n;
135684917b87SBrian Somers   char *name, *oname;
13576f384573SBrian Somers 
13586f384573SBrian Somers   n = strlen(dl->name);
13596f384573SBrian Somers   name = (char *)malloc(n+3);
13606f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
13616f384573SBrian Somers     if (!isdigit(dl->name[f]))
13626f384573SBrian Somers       break;
13636f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
13646f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
136584917b87SBrian Somers   oname = dl->name;
136654cd8e13SBrian Somers   dl->name = name;
136754cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
136884917b87SBrian Somers   return oname;
13696f384573SBrian Somers }
1370dd0645c5SBrian Somers 
1371dd0645c5SBrian Somers int
1372dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1373dd0645c5SBrian Somers {
1374dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1375dd0645c5SBrian Somers     return 0;
1376dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1377dd0645c5SBrian Somers     dl->script.run = 0;
1378dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1379dd0645c5SBrian Somers     dl->reconnect_tries = 0;
138081358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1381dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1382dd0645c5SBrian Somers   return 1;
1383dd0645c5SBrian Somers }
1384c11e57a3SBrian Somers 
1385c11e57a3SBrian Somers int
1386c11e57a3SBrian Somers datalink_GetDialTimeout(struct datalink *dl)
1387c11e57a3SBrian Somers {
1388c11e57a3SBrian Somers   int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
1389c11e57a3SBrian Somers 
1390c11e57a3SBrian Somers   if (dl->dial.incs < dl->cfg.dial.maxinc)
1391c11e57a3SBrian Somers     dl->dial.incs++;
1392c11e57a3SBrian Somers 
1393c11e57a3SBrian Somers   return result;
1394c11e57a3SBrian Somers }
1395