xref: /freebsd/usr.sbin/ppp/datalink.c (revision 0ca6f91b)
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  *
260ca6f91bSBrian Somers  *	$Id: datalink.c,v 1.36 1999/04/05 21:52: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 
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"
61972a1bcfSBrian Somers #ifndef NORADIUS
62972a1bcfSBrian Somers #include "radius.h"
63972a1bcfSBrian Somers #endif
643006ec67SBrian Somers #include "bundle.h"
653006ec67SBrian Somers #include "chat.h"
66e2ebb036SBrian Somers #include "auth.h"
673006ec67SBrian Somers #include "modem.h"
68c7cc5030SBrian Somers #include "prompt.h"
69e2ebb036SBrian Somers #include "lcpproto.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 &&
117dd7e2610SBrian Somers       physical_GetFD(dl->physical) != -1) {
118030e4ebbSBrian Somers     /* Don't close our modem if the link is dedicated */
119030e4ebbSBrian Somers     datalink_LoginDone(dl);
120030e4ebbSBrian Somers     return;
121030e4ebbSBrian Somers   }
122030e4ebbSBrian Somers 
1233006ec67SBrian Somers   modem_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 
178f9545805SBrian Somers static 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';
187f9545805SBrian Somers       dl->phone.next = dl->phone.list;
188f9545805SBrian Somers     }
189f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
190f9545805SBrian Somers   }
191f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
192f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
193f9545805SBrian Somers   if (*phone)
194dd7e2610SBrian Somers     log_Printf(LogPHASE, "Phone: %s\n", phone);
195f9545805SBrian Somers   return phone;
196f9545805SBrian Somers }
197f9545805SBrian Somers 
198c5a5a6caSBrian Somers static void
199c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
200c5a5a6caSBrian Somers {
201d345321bSBrian Somers   if (!dl->script.packetmode) {
202c11e57a3SBrian Somers     dl->dial.tries = -1;
203c11e57a3SBrian Somers     dl->dial.incs = 0;
2049ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
205d345321bSBrian Somers   } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
206c11e57a3SBrian Somers     dl->dial.tries = 0;
207dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
208c5a5a6caSBrian Somers     if (dl->script.run) {
2099ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
210c5a5a6caSBrian Somers       modem_Offline(dl->physical);
211f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
212030e4ebbSBrian Somers     } else {
213d4af231cSBrian Somers       timer_Stop(&dl->physical->Timer);
214030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
215030e4ebbSBrian Somers         /* force a redial timeout */
216030e4ebbSBrian Somers         modem_Close(dl->physical);
217c5a5a6caSBrian Somers       datalink_HangupDone(dl);
218030e4ebbSBrian Somers     }
219c5a5a6caSBrian Somers   } else {
220c11e57a3SBrian Somers     dl->dial.tries = -1;
221c11e57a3SBrian Somers     dl->dial.incs = 0;
222c7cc5030SBrian Somers 
223643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
2243b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
2253b0f8d2eSBrian Somers 
226cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
227cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
2283b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
229503a7782SBrian Somers 
2309ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
231dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
232dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
233c5a5a6caSBrian Somers   }
234c5a5a6caSBrian Somers }
235c5a5a6caSBrian Somers 
2363006ec67SBrian Somers static int
2373006ec67SBrian Somers datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
2383006ec67SBrian Somers                    int *n)
2393006ec67SBrian Somers {
2403006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2413006ec67SBrian Somers   int result;
2423006ec67SBrian Somers 
243310c3babSBrian Somers   result = 0;
2443006ec67SBrian Somers   switch (dl->state) {
2453006ec67SBrian Somers     case DATALINK_CLOSED:
24681358fa3SBrian Somers       if ((dl->physical->type &
24781358fa3SBrian Somers            (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
248565e35e5SBrian Somers           !bundle_IsDead(dl->bundle))
249565e35e5SBrian Somers         /*
25081358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
25181358fa3SBrian Somers          * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
25281358fa3SBrian Somers          * Go to DATALINK_OPENING via datalink_Up() and fall through.
253565e35e5SBrian Somers          */
254565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
255565e35e5SBrian Somers       else
256310c3babSBrian Somers         break;
257565e35e5SBrian Somers       /* fall through */
2583006ec67SBrian Somers 
2593006ec67SBrian Somers     case DATALINK_OPENING:
260c11e57a3SBrian Somers       if (dl->dial.timer.state != TIMER_RUNNING) {
261c11e57a3SBrian Somers         if (--dl->dial.tries < 0)
262c11e57a3SBrian Somers           dl->dial.tries = 0;
2633006ec67SBrian Somers         if (modem_Open(dl->physical, dl->bundle) >= 0) {
264bf1d3ff6SBrian Somers           log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
265bf1d3ff6SBrian Somers                            "Type `~?' for help\r\n", dl->name,
266bf1d3ff6SBrian Somers                            dl->physical->name.full);
267c5a5a6caSBrian Somers           if (dl->script.run) {
2689ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
269f9545805SBrian Somers             chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
270f9545805SBrian Somers                       datalink_ChoosePhoneNumber(dl));
27181358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
272565e35e5SBrian Somers                 dl->cfg.dial.max)
273dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
274c11e57a3SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial.tries,
275565e35e5SBrian Somers                         dl->cfg.dial.max);
276c5a5a6caSBrian Somers           } else
277c5a5a6caSBrian Somers             datalink_LoginDone(dl);
2788b09cf1cSBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
2793006ec67SBrian Somers         } else {
28081358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
281565e35e5SBrian Somers               dl->cfg.dial.max)
282dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
283c11e57a3SBrian Somers                        dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
2843006ec67SBrian Somers           else
285dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem\n");
2863006ec67SBrian Somers 
2873b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
28881358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
289c11e57a3SBrian Somers                dl->cfg.dial.max && dl->dial.tries == 0)) {
2909ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
2913006ec67SBrian Somers             dl->reconnect_tries = 0;
292c11e57a3SBrian Somers             dl->dial.tries = -1;
293bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s\n",
294bf1d3ff6SBrian Somers                              dl->physical->name.full);
2955b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
296c5a5a6caSBrian Somers           }
297bf1d3ff6SBrian Somers           if (!dl->bundle->CleaningUp) {
298c11e57a3SBrian Somers             int timeout;
299c11e57a3SBrian Somers 
300c11e57a3SBrian Somers             timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
301bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
302b5c347a3SBrian Somers                              dl->physical->name.full, timeout);
3033006ec67SBrian Somers           }
3043006ec67SBrian Somers         }
305bf1d3ff6SBrian Somers       }
306310c3babSBrian Somers       break;
3073006ec67SBrian Somers 
3083006ec67SBrian Somers     case DATALINK_HANGUP:
3093006ec67SBrian Somers     case DATALINK_DIAL:
3103006ec67SBrian Somers     case DATALINK_LOGIN:
3113006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
3123006ec67SBrian Somers       switch (dl->chat.state) {
3133006ec67SBrian Somers         case CHAT_DONE:
3143006ec67SBrian Somers           /* script succeeded */
31539d94652SBrian Somers           chat_Destroy(&dl->chat);
3163006ec67SBrian Somers           switch(dl->state) {
3173006ec67SBrian Somers             case DATALINK_HANGUP:
3183006ec67SBrian Somers               datalink_HangupDone(dl);
3193006ec67SBrian Somers               break;
3203006ec67SBrian Somers             case DATALINK_DIAL:
3219ae58882SBrian Somers               datalink_NewState(dl, DATALINK_LOGIN);
322f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
3231342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3243006ec67SBrian Somers             case DATALINK_LOGIN:
32547dd77c1SBrian Somers               dl->phone.alt = NULL;
326c5a5a6caSBrian Somers               datalink_LoginDone(dl);
327b51a60ccSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3283006ec67SBrian Somers           }
3293006ec67SBrian Somers           break;
3303006ec67SBrian Somers         case CHAT_FAILED:
3313006ec67SBrian Somers           /* Going down - script failed */
332dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
33339d94652SBrian Somers           chat_Destroy(&dl->chat);
3343006ec67SBrian Somers           switch(dl->state) {
3353006ec67SBrian Somers             case DATALINK_HANGUP:
3363006ec67SBrian Somers               datalink_HangupDone(dl);
3373006ec67SBrian Somers               break;
3383006ec67SBrian Somers             case DATALINK_DIAL:
3393006ec67SBrian Somers             case DATALINK_LOGIN:
3409ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
341d4af231cSBrian Somers               modem_Offline(dl->physical);	/* Is this required ? */
342f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
3431342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3443006ec67SBrian Somers           }
3453006ec67SBrian Somers           break;
3463006ec67SBrian Somers       }
3473006ec67SBrian Somers       break;
348c7cc5030SBrian Somers 
349c7cc5030SBrian Somers     case DATALINK_READY:
350e2ebb036SBrian Somers     case DATALINK_LCP:
351e2ebb036SBrian Somers     case DATALINK_AUTH:
35292b09558SBrian Somers     case DATALINK_CBCP:
3533006ec67SBrian Somers     case DATALINK_OPEN:
35458330d7bSBrian Somers       result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
35558330d7bSBrian Somers                descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3563006ec67SBrian Somers       break;
3573006ec67SBrian Somers   }
3583006ec67SBrian Somers   return result;
3593006ec67SBrian Somers }
3603006ec67SBrian Somers 
361ea722969SBrian Somers int
362ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
363ea722969SBrian Somers {
364ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
365ea722969SBrian Somers }
366ea722969SBrian Somers 
3673006ec67SBrian Somers static int
3682f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3693006ec67SBrian Somers {
3703006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3713006ec67SBrian Somers 
3723006ec67SBrian Somers   switch (dl->state) {
3733006ec67SBrian Somers     case DATALINK_CLOSED:
374c7cc5030SBrian Somers     case DATALINK_OPENING:
3753006ec67SBrian Somers       break;
376c7cc5030SBrian Somers 
3773006ec67SBrian Somers     case DATALINK_HANGUP:
3783006ec67SBrian Somers     case DATALINK_DIAL:
3793006ec67SBrian Somers     case DATALINK_LOGIN:
3803006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
381c7cc5030SBrian Somers 
382c7cc5030SBrian Somers     case DATALINK_READY:
383e2ebb036SBrian Somers     case DATALINK_LCP:
384e2ebb036SBrian Somers     case DATALINK_AUTH:
38592b09558SBrian Somers     case DATALINK_CBCP:
3863006ec67SBrian Somers     case DATALINK_OPEN:
38758330d7bSBrian Somers       return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
38858330d7bSBrian Somers              descriptor_IsSet(&dl->physical->desc, fdset);
3893006ec67SBrian Somers   }
3903006ec67SBrian Somers   return 0;
3913006ec67SBrian Somers }
3923006ec67SBrian Somers 
3933006ec67SBrian Somers static void
3943006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
3953006ec67SBrian Somers {
3963006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3973006ec67SBrian Somers 
3983006ec67SBrian Somers   switch (dl->state) {
3993006ec67SBrian Somers     case DATALINK_CLOSED:
400c7cc5030SBrian Somers     case DATALINK_OPENING:
4013006ec67SBrian Somers       break;
402c7cc5030SBrian Somers 
4033006ec67SBrian Somers     case DATALINK_HANGUP:
4043006ec67SBrian Somers     case DATALINK_DIAL:
4053006ec67SBrian Somers     case DATALINK_LOGIN:
4063006ec67SBrian Somers       descriptor_Read(&dl->chat.desc, bundle, fdset);
4073006ec67SBrian Somers       break;
408c7cc5030SBrian Somers 
409c7cc5030SBrian Somers     case DATALINK_READY:
410e2ebb036SBrian Somers     case DATALINK_LCP:
411e2ebb036SBrian Somers     case DATALINK_AUTH:
41292b09558SBrian Somers     case DATALINK_CBCP:
4133006ec67SBrian Somers     case DATALINK_OPEN:
41458330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
41558330d7bSBrian Somers         descriptor_Read(&dl->chap.desc, bundle, fdset);
41658330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
4173006ec67SBrian Somers         descriptor_Read(&dl->physical->desc, bundle, fdset);
4183006ec67SBrian Somers       break;
4193006ec67SBrian Somers   }
4203006ec67SBrian Somers }
4213006ec67SBrian Somers 
4221af29a6eSBrian Somers static int
423f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
4243006ec67SBrian Somers {
4253006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4261af29a6eSBrian Somers   int result = 0;
4273006ec67SBrian Somers 
4283006ec67SBrian Somers   switch (dl->state) {
4293006ec67SBrian Somers     case DATALINK_CLOSED:
430c7cc5030SBrian Somers     case DATALINK_OPENING:
4313006ec67SBrian Somers       break;
432c7cc5030SBrian Somers 
4333006ec67SBrian Somers     case DATALINK_HANGUP:
4343006ec67SBrian Somers     case DATALINK_DIAL:
4353006ec67SBrian Somers     case DATALINK_LOGIN:
4361af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4373006ec67SBrian Somers       break;
438c7cc5030SBrian Somers 
439c7cc5030SBrian Somers     case DATALINK_READY:
440e2ebb036SBrian Somers     case DATALINK_LCP:
441e2ebb036SBrian Somers     case DATALINK_AUTH:
44292b09558SBrian Somers     case DATALINK_CBCP:
4433006ec67SBrian Somers     case DATALINK_OPEN:
44458330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
44558330d7bSBrian Somers         result += descriptor_Write(&dl->chap.desc, bundle, fdset);
44658330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
44758330d7bSBrian Somers         result += descriptor_Write(&dl->physical->desc, bundle, fdset);
4483006ec67SBrian Somers       break;
4493006ec67SBrian Somers   }
4501af29a6eSBrian Somers 
4511af29a6eSBrian Somers   return result;
4523006ec67SBrian Somers }
4533006ec67SBrian Somers 
4546d666775SBrian Somers static void
4559c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
456e2ebb036SBrian Somers {
4579c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
458c11e57a3SBrian Somers     dl->dial.tries = -1;
459e2ebb036SBrian Somers     dl->reconnect_tries = 0;
4607729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4619c81b87dSBrian Somers       dl->stayonline = 1;
462e2ebb036SBrian Somers   }
463e2ebb036SBrian Somers 
4647729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4659c81b87dSBrian Somers     dl->stayonline = 0;
466d4af231cSBrian Somers     timer_Stop(&dl->physical->Timer);
4679c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4689c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
469e2ebb036SBrian Somers     modem_Offline(dl->physical);
470b6f5f442SBrian Somers     chat_Destroy(&dl->chat);
471e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4729ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
473f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
474e2ebb036SBrian Somers     } else
475e2ebb036SBrian Somers       datalink_HangupDone(dl);
476e2ebb036SBrian Somers   }
477e2ebb036SBrian Somers }
478e2ebb036SBrian Somers 
479e2ebb036SBrian Somers static void
4806d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4816d666775SBrian Somers {
4826d666775SBrian Somers   /* The given FSM is about to start up ! */
4836d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
484a611cad6SBrian Somers 
48592e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
486e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
487e2ebb036SBrian Somers }
4886d666775SBrian Somers 
4896d666775SBrian Somers static void
4906d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4916d666775SBrian Somers {
4926d666775SBrian Somers   /* The given fsm is now up */
4936d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4945e315498SBrian Somers   struct lcp *lcp = &dl->physical->link.lcp;
4956d666775SBrian Somers 
49692e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
497f0cdd9c0SBrian Somers     datalink_GotAuthname(dl, "");
4985e315498SBrian Somers     lcp->auth_ineed = lcp->want_auth;
4995e315498SBrian Somers     lcp->auth_iwait = lcp->his_auth;
5005e315498SBrian Somers     if (lcp->his_auth || lcp->want_auth) {
5015945a079SBrian Somers       if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
5025563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
503dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
5045e315498SBrian Somers                 Auth2Nam(lcp->his_auth, lcp->his_authtype),
5055e315498SBrian Somers                 Auth2Nam(lcp->want_auth, lcp->want_authtype));
5065e315498SBrian Somers       if (lcp->his_auth == PROTO_PAP)
507f0cdd9c0SBrian Somers         auth_StartReq(&dl->pap);
5085e315498SBrian Somers       if (lcp->want_auth == PROTO_CHAP)
509f0cdd9c0SBrian Somers         auth_StartReq(&dl->chap.auth);
510e2ebb036SBrian Somers     } else
511e2ebb036SBrian Somers       datalink_AuthOk(dl);
512e2ebb036SBrian Somers   }
513e2ebb036SBrian Somers }
514e2ebb036SBrian Somers 
51564cfdfc6SBrian Somers static void
51664cfdfc6SBrian Somers datalink_AuthReInit(struct datalink *dl)
51764cfdfc6SBrian Somers {
51864cfdfc6SBrian Somers   auth_StopTimer(&dl->pap);
51964cfdfc6SBrian Somers   auth_StopTimer(&dl->chap.auth);
52064cfdfc6SBrian Somers   chap_ReInit(&dl->chap);
52164cfdfc6SBrian Somers }
52264cfdfc6SBrian Somers 
523e2ebb036SBrian Somers void
524f0cdd9c0SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name)
525643f4904SBrian Somers {
526f0cdd9c0SBrian Somers   strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
527f0cdd9c0SBrian Somers   dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
528643f4904SBrian Somers }
529643f4904SBrian Somers 
530643f4904SBrian Somers void
53192b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
532e2ebb036SBrian Somers {
53306337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5341df0a3b9SBrian Somers 
535dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5361fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5371fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5381fa665f5SBrian Somers       case MP_LINKSENT:
539ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5401fa665f5SBrian Somers         return;
5411fa665f5SBrian Somers       case MP_UP:
5420a1b5c9dSBrian Somers         /* First link in the bundle */
54392b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
5440a1b5c9dSBrian Somers         /* fall through */
5451fa665f5SBrian Somers       case MP_ADDED:
5460a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5471df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
5481fa665f5SBrian Somers         break;
5491fa665f5SBrian Somers       case MP_FAILED:
55049052c95SBrian Somers         datalink_AuthNotOk(dl);
55149052c95SBrian Somers         return;
55249052c95SBrian Somers     }
55349052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
554dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
555897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
556897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
55749052c95SBrian Somers     return;
55850abd4c8SBrian Somers   } else {
55950abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
560ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
56192b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
56250abd4c8SBrian Somers   }
56349052c95SBrian Somers 
56406337856SBrian Somers   if (ccpok) {
565dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
566dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
56706337856SBrian Somers   }
5689ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5693b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5703b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5716d666775SBrian Somers }
572e2ebb036SBrian Somers 
573e2ebb036SBrian Somers void
57492b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
57592b09558SBrian Somers {
57692b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
57764cfdfc6SBrian Somers   datalink_AuthReInit(dl);
57892b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
57992b09558SBrian Somers }
58092b09558SBrian Somers 
58192b09558SBrian Somers void
58292b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
58392b09558SBrian Somers {
58492b09558SBrian Somers   cbcp_Down(&dl->cbcp);
58592b09558SBrian Somers   datalink_CBCPComplete(dl);
58692b09558SBrian Somers }
58792b09558SBrian Somers 
58892b09558SBrian Somers void
58992b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
59092b09558SBrian Somers {
5915165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
59292b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
5935165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
5945165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
5955165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
5965165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
5975165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
59892b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
59992b09558SBrian Somers     cbcp_Up(&dl->cbcp);
60092b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
6015165af6fSBrian Somers     /* It's not CBCP */
60292b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
60392b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
60464cfdfc6SBrian Somers     datalink_AuthReInit(dl);
60592b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
60692b09558SBrian Somers   } else
60792b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
60892b09558SBrian Somers       case 0:
60992b09558SBrian Somers         datalink_NCPUp(dl);
61092b09558SBrian Somers         break;
61192b09558SBrian Somers 
61292b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
61392b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
61492b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
61592b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
61692b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
61792b09558SBrian Somers                      dl->peer.authname);
61892b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
61992b09558SBrian Somers         } else {
62092b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
62192b09558SBrian Somers           if (ptr)
62292b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
62392b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
62492b09558SBrian Somers                      dl->cbcp.fsm.phone);
62592b09558SBrian Somers           dl->cbcp.required = 1;
62692b09558SBrian Somers         }
62792b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
62892b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
62964cfdfc6SBrian Somers         datalink_AuthReInit(dl);
63092b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
63192b09558SBrian Somers         break;
63292b09558SBrian Somers 
63392b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
63492b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
63592b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
63692b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
63792b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
63892b09558SBrian Somers                    dl->cbcp.fsm.phone);
63992b09558SBrian Somers         dl->cbcp.required = 1;
64092b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
64192b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
64264cfdfc6SBrian Somers         datalink_AuthReInit(dl);
64392b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
64492b09558SBrian Somers         break;
64592b09558SBrian Somers 
64692b09558SBrian Somers       default:
64792b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
64892b09558SBrian Somers                    dl->name);
64992b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
65064cfdfc6SBrian Somers         datalink_AuthReInit(dl);
65192b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
65292b09558SBrian Somers         break;
65392b09558SBrian Somers     }
65492b09558SBrian Somers }
65592b09558SBrian Somers 
65692b09558SBrian Somers void
657e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
658e2ebb036SBrian Somers {
6599ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
66064cfdfc6SBrian Somers   datalink_AuthReInit(dl);
661dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6626d666775SBrian Somers }
6636d666775SBrian Somers 
6646d666775SBrian Somers static void
6656d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6666d666775SBrian Somers {
6676d666775SBrian Somers   /* The given FSM has been told to come down */
6686d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
669a611cad6SBrian Somers 
67092e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
671e2ebb036SBrian Somers     switch (dl->state) {
672e2ebb036SBrian Somers       case DATALINK_OPEN:
673643f4904SBrian Somers         peerid_Init(&dl->peer);
67409206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
675ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
676e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
67792b09558SBrian Somers         /* fall through (just in case) */
67892b09558SBrian Somers 
67992b09558SBrian Somers       case DATALINK_CBCP:
68092b09558SBrian Somers         if (!dl->cbcp.required)
68192b09558SBrian Somers           cbcp_Down(&dl->cbcp);
68292b09558SBrian Somers         /* fall through (just in case) */
683e2ebb036SBrian Somers 
684e2ebb036SBrian Somers       case DATALINK_AUTH:
685dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
686dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
6876d666775SBrian Somers     }
6889ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
68964cfdfc6SBrian Somers     datalink_AuthReInit(dl);
690e2ebb036SBrian Somers   }
6916d666775SBrian Somers }
6926d666775SBrian Somers 
6936d666775SBrian Somers static void
6946d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
6956d666775SBrian Somers {
6966d666775SBrian Somers   /* The given fsm is now down */
6976d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
6986d666775SBrian Somers 
69992e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
70009206a6fSBrian Somers     fsm2initial(fp);
7016d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
7029c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
7030a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
7040a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
7056d666775SBrian Somers }
7066d666775SBrian Somers 
7073006ec67SBrian Somers struct datalink *
7086f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
7093006ec67SBrian Somers {
7103006ec67SBrian Somers   struct datalink *dl;
7113006ec67SBrian Somers 
7123006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
7133006ec67SBrian Somers   if (dl == NULL)
7143006ec67SBrian Somers     return dl;
7153006ec67SBrian Somers 
7163006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
7173006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
7183006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
7193006ec67SBrian Somers   dl->desc.Read = datalink_Read;
7203006ec67SBrian Somers   dl->desc.Write = datalink_Write;
7215b8b8060SBrian Somers 
722e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
723e718d1d7SBrian Somers 
72473a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
72573a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
72673a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
727f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
728f9545805SBrian Somers   *dl->phone.list = '\0';
729f9545805SBrian Somers   dl->phone.next = NULL;
730f9545805SBrian Somers   dl->phone.alt = NULL;
731f9545805SBrian Somers   dl->phone.chosen = "N/A";
7329c81b87dSBrian Somers   dl->stayonline = 0;
733c7cc5030SBrian Somers   dl->script.run = 1;
734c7cc5030SBrian Somers   dl->script.packetmode = 1;
7353b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
7365b8b8060SBrian Somers 
7373006ec67SBrian Somers   dl->bundle = bundle;
7383006ec67SBrian Somers   dl->next = NULL;
739e718d1d7SBrian Somers 
740c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
741e718d1d7SBrian Somers 
742c11e57a3SBrian Somers   dl->dial.tries = 0;
743565e35e5SBrian Somers   dl->cfg.dial.max = 1;
744565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
745565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
746c11e57a3SBrian Somers   dl->cfg.dial.inc = 0;
747c11e57a3SBrian Somers   dl->cfg.dial.maxinc = 10;
748e718d1d7SBrian Somers 
749abff9baeSBrian Somers   dl->reconnect_tries = 0;
750565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
751565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
752abff9baeSBrian Somers 
75392b09558SBrian Somers   dl->cfg.callback.opmask = 0;
75492b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
75592b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
75692b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
75792b09558SBrian Somers 
7583006ec67SBrian Somers   dl->name = strdup(name);
759643f4904SBrian Somers   peerid_Init(&dl->peer);
7606f384573SBrian Somers   dl->parent = &bundle->fsm;
7613b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7623b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7633b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7643b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7653b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7663b0f8d2eSBrian Somers 
767565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
7683006ec67SBrian Somers     free(dl->name);
7693006ec67SBrian Somers     free(dl);
7703006ec67SBrian Somers     return NULL;
7713006ec67SBrian Somers   }
772f0cdd9c0SBrian Somers 
773f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
774f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
77592b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
776f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
7773006ec67SBrian Somers 
7789ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
7799ae58882SBrian Somers              dl->name, datalink_State(dl));
7803006ec67SBrian Somers 
7813006ec67SBrian Somers   return dl;
7823006ec67SBrian Somers }
7833006ec67SBrian Somers 
7843006ec67SBrian Somers struct datalink *
785cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
786cd7bd93aSBrian Somers {
787cd7bd93aSBrian Somers   struct datalink *dl;
788cd7bd93aSBrian Somers 
789cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
790cd7bd93aSBrian Somers   if (dl == NULL)
791cd7bd93aSBrian Somers     return dl;
792cd7bd93aSBrian Somers 
793cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
794cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
795cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
796cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
797cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
798cd7bd93aSBrian Somers 
799cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
800cd7bd93aSBrian Somers 
801cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
802cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
803cd7bd93aSBrian Somers   *dl->phone.list = '\0';
804643f4904SBrian Somers   dl->phone.next = NULL;
805643f4904SBrian Somers   dl->phone.alt = NULL;
806643f4904SBrian Somers   dl->phone.chosen = "N/A";
807cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
808cd7bd93aSBrian Somers   dl->next = NULL;
809c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
810c11e57a3SBrian Somers   dl->dial.tries = 0;
811cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
812cd7bd93aSBrian Somers   dl->name = strdup(name);
813643f4904SBrian Somers   peerid_Init(&dl->peer);
814cd7bd93aSBrian Somers   dl->parent = odl->parent;
815cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
816643f4904SBrian Somers   dl->fsmp.object = dl;
817cd7bd93aSBrian Somers 
81881358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
819cd7bd93aSBrian Somers     free(dl->name);
820cd7bd93aSBrian Somers     free(dl);
821cd7bd93aSBrian Somers     return NULL;
822cd7bd93aSBrian Somers   }
823f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
824479508cfSBrian Somers   dl->pap.cfg = odl->pap.cfg;
825f0cdd9c0SBrian Somers 
826f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
827479508cfSBrian Somers   dl->chap.auth.cfg = odl->chap.auth.cfg;
828f0cdd9c0SBrian Somers 
829cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
830cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
831cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
832cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
833cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
834cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
835cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
836cd7bd93aSBrian Somers 
83792b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
838cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
839cd7bd93aSBrian Somers 
8409ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
8419ae58882SBrian Somers              dl->name, datalink_State(dl));
842cd7bd93aSBrian Somers 
843cd7bd93aSBrian Somers   return dl;
844cd7bd93aSBrian Somers }
845cd7bd93aSBrian Somers 
846cd7bd93aSBrian Somers struct datalink *
8473006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
8483006ec67SBrian Somers {
8493006ec67SBrian Somers   struct datalink *result;
8503006ec67SBrian Somers 
85139d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
852dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
853c7cc5030SBrian Somers               datalink_State(dl));
85439d94652SBrian Somers     switch (dl->state) {
85539d94652SBrian Somers       case DATALINK_HANGUP:
85639d94652SBrian Somers       case DATALINK_DIAL:
85739d94652SBrian Somers       case DATALINK_LOGIN:
85839d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
85939d94652SBrian Somers         break;
86039d94652SBrian Somers     }
86139d94652SBrian Somers   }
8623006ec67SBrian Somers 
863c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
8643006ec67SBrian Somers   result = dl->next;
8653b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
8663006ec67SBrian Somers   free(dl->name);
8673006ec67SBrian Somers   free(dl);
8683006ec67SBrian Somers 
8693006ec67SBrian Somers   return result;
8703006ec67SBrian Somers }
8713006ec67SBrian Somers 
8723006ec67SBrian Somers void
873c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8743006ec67SBrian Somers {
8756f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
876565e35e5SBrian Somers     /* Ignore scripts */
877565e35e5SBrian Somers     runscripts = 0;
878565e35e5SBrian Somers 
879c7cc5030SBrian Somers   switch (dl->state) {
880c7cc5030SBrian Somers     case DATALINK_CLOSED:
8813b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
8823b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
8833b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
8849ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
885565e35e5SBrian Somers       dl->reconnect_tries =
8866f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
887c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
888c5a5a6caSBrian Somers       dl->script.run = runscripts;
889c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
890c7cc5030SBrian Somers       break;
891c7cc5030SBrian Somers 
892c7cc5030SBrian Somers     case DATALINK_OPENING:
893c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
894c7cc5030SBrian Somers         dl->script.run = 1;
895c7cc5030SBrian Somers       /* fall through */
896c7cc5030SBrian Somers 
897c7cc5030SBrian Somers     case DATALINK_DIAL:
898c7cc5030SBrian Somers     case DATALINK_LOGIN:
899c7cc5030SBrian Somers     case DATALINK_READY:
900c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
901c7cc5030SBrian Somers         dl->script.packetmode = 1;
902c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
903c7cc5030SBrian Somers           datalink_LoginDone(dl);
904c7cc5030SBrian Somers       }
905c7cc5030SBrian Somers       break;
9063006ec67SBrian Somers   }
9073006ec67SBrian Somers }
9083006ec67SBrian Somers 
9093006ec67SBrian Somers void
9109c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
911c7cc5030SBrian Somers {
912c7cc5030SBrian Somers   /* Please close */
913e2ebb036SBrian Somers   switch (dl->state) {
914e2ebb036SBrian Somers     case DATALINK_OPEN:
915643f4904SBrian Somers       peerid_Init(&dl->peer);
91609206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
917e2ebb036SBrian Somers       /* fall through */
918e2ebb036SBrian Somers 
91992b09558SBrian Somers     case DATALINK_CBCP:
920e2ebb036SBrian Somers     case DATALINK_AUTH:
921e2ebb036SBrian Somers     case DATALINK_LCP:
92264cfdfc6SBrian Somers       datalink_AuthReInit(dl);
923dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
9249c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
925c11e57a3SBrian Somers         dl->dial.tries = -1;
926c7cc5030SBrian Somers         dl->reconnect_tries = 0;
9279c81b87dSBrian Somers         if (how == CLOSE_LCP)
9289c81b87dSBrian Somers           dl->stayonline = 1;
929c7cc5030SBrian Somers       }
930e2ebb036SBrian Somers       break;
931e2ebb036SBrian Somers 
932e2ebb036SBrian Somers     default:
9339c81b87dSBrian Somers       datalink_ComeDown(dl, how);
934c7cc5030SBrian Somers   }
935e2ebb036SBrian Somers }
936c7cc5030SBrian Somers 
937c7cc5030SBrian Somers void
9389c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
939c7cc5030SBrian Somers {
940c7cc5030SBrian Somers   /* Carrier is lost */
941e2ebb036SBrian Somers   switch (dl->state) {
942e2ebb036SBrian Somers     case DATALINK_OPEN:
943643f4904SBrian Somers       peerid_Init(&dl->peer);
94409206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
945e2ebb036SBrian Somers       /* fall through */
946e2ebb036SBrian Somers 
94792b09558SBrian Somers     case DATALINK_CBCP:
948e2ebb036SBrian Somers     case DATALINK_AUTH:
949e2ebb036SBrian Somers     case DATALINK_LCP:
95009206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
951e2ebb036SBrian Somers       /* fall through */
952c7cc5030SBrian Somers 
953e2ebb036SBrian Somers     default:
9549c81b87dSBrian Somers       datalink_ComeDown(dl, how);
955c7cc5030SBrian Somers   }
956e2ebb036SBrian Somers }
957c7cc5030SBrian Somers 
958c7cc5030SBrian Somers void
9593006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9603006ec67SBrian Somers {
9613006ec67SBrian Somers   dl->reconnect_tries = 0;
9623006ec67SBrian Somers }
963c7cc5030SBrian Somers 
9649c81b87dSBrian Somers void
9659c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9669c81b87dSBrian Somers {
9677729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9689c81b87dSBrian Somers     dl->stayonline = 1;
9699c81b87dSBrian Somers }
9709c81b87dSBrian Somers 
971643f4904SBrian Somers int
972643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
973c7cc5030SBrian Somers {
974643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
975643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
976643f4904SBrian Somers                 datalink_State(arg->cx));
977643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
978643f4904SBrian Somers   if (*arg->cx->peer.authname)
979643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
980643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
981643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
982565e35e5SBrian Somers   else
983643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
984643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
985643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
986643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
987643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
988643f4904SBrian Somers 
989643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
990643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
991643f4904SBrian Somers                 arg->cx->cfg.phone.list);
992643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
993643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
994643f4904SBrian Somers                   arg->cx->cfg.dial.max);
995565e35e5SBrian Somers   else
996643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
997b5c347a3SBrian Somers   if (arg->cx->cfg.dial.next_timeout >= 0)
998643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
999565e35e5SBrian Somers   else
1000643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
1001b5c347a3SBrian Somers   if (arg->cx->cfg.dial.timeout >= 0)
1002643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
1003565e35e5SBrian Somers   else
1004643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
1005643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
1006643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
1007643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
1008643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
1009643f4904SBrian Somers   else
1010643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
101192b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
101292b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
101392b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
101492b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
101592b09558SBrian Somers   else {
101692b09558SBrian Somers     int comma = 0;
101792b09558SBrian Somers 
101892b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
101992b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
102092b09558SBrian Somers       comma = 1;
102192b09558SBrian Somers     }
102292b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
102392b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
102492b09558SBrian Somers       comma = 1;
102592b09558SBrian Somers     }
102692b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
102792b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
102892b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
102992b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
103092b09558SBrian Somers       comma = 1;
103192b09558SBrian Somers     }
103292b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
103392b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
103492b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
103592b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
1036cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
1037cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
1038cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
1039cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
1040cf784a89SBrian Somers         else
1041cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
1042cf784a89SBrian Somers       } else
1043cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
104492b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
104592b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
104692b09558SBrian Somers     } else
104792b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
104892b09558SBrian Somers   }
104992b09558SBrian Somers 
1050643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1051643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1052643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1053643f4904SBrian Somers                 arg->cx->cfg.script.login);
1054643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1055643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1056643f4904SBrian Somers   return 0;
1057565e35e5SBrian Somers }
1058565e35e5SBrian Somers 
1059565e35e5SBrian Somers int
1060565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1061565e35e5SBrian Somers {
106225092092SBrian Somers   if (arg->argc == arg->argn+2) {
106325092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
106425092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1065565e35e5SBrian Somers     return 0;
1066565e35e5SBrian Somers   }
1067565e35e5SBrian Somers   return -1;
1068565e35e5SBrian Somers }
1069565e35e5SBrian Somers 
1070565e35e5SBrian Somers int
1071565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1072565e35e5SBrian Somers {
1073c11e57a3SBrian Somers   const char *sep, *osep;
1074c11e57a3SBrian Somers   int timeout, inc, maxinc, tries;
1075565e35e5SBrian Somers 
107625092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
107725092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
107825092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1079565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1080565e35e5SBrian Somers       randinit();
1081565e35e5SBrian Somers     } else {
108225092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1083565e35e5SBrian Somers 
1084565e35e5SBrian Somers       if (timeout >= 0)
1085565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1086565e35e5SBrian Somers       else {
1087dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1088565e35e5SBrian Somers 	return -1;
1089565e35e5SBrian Somers       }
1090565e35e5SBrian Somers     }
1091565e35e5SBrian Somers 
1092c11e57a3SBrian Somers     sep = strchr(arg->argv[arg->argn], '+');
1093c11e57a3SBrian Somers     if (sep) {
1094c11e57a3SBrian Somers       inc = atoi(++sep);
1095c11e57a3SBrian Somers       osep = sep;
1096c11e57a3SBrian Somers       if (inc >= 0)
1097c11e57a3SBrian Somers         arg->cx->cfg.dial.inc = inc;
1098c11e57a3SBrian Somers       else {
1099c11e57a3SBrian Somers         log_Printf(LogWARN, "Invalid timeout increment\n");
1100c11e57a3SBrian Somers         return -1;
1101c11e57a3SBrian Somers       }
1102c11e57a3SBrian Somers       sep = strchr(sep, '-');
1103c11e57a3SBrian Somers       if (sep) {
1104c11e57a3SBrian Somers         maxinc = atoi(++sep);
1105c11e57a3SBrian Somers         if (maxinc >= 0)
1106c11e57a3SBrian Somers           arg->cx->cfg.dial.maxinc = maxinc;
1107c11e57a3SBrian Somers         else {
1108c11e57a3SBrian Somers           log_Printf(LogWARN, "Invalid maximum timeout increments\n");
1109c11e57a3SBrian Somers           return -1;
1110c11e57a3SBrian Somers         }
1111c11e57a3SBrian Somers       } else {
1112c11e57a3SBrian Somers         /* Default timeout increment */
1113c11e57a3SBrian Somers         arg->cx->cfg.dial.maxinc = 10;
1114c11e57a3SBrian Somers         sep = osep;
1115c11e57a3SBrian Somers       }
1116c11e57a3SBrian Somers     } else {
1117c11e57a3SBrian Somers       /* Default timeout increment & max increment */
1118c11e57a3SBrian Somers       arg->cx->cfg.dial.inc = 0;
1119c11e57a3SBrian Somers       arg->cx->cfg.dial.maxinc = 10;
1120c11e57a3SBrian Somers       sep = arg->argv[arg->argn];
1121c11e57a3SBrian Somers     }
1122c11e57a3SBrian Somers 
1123c11e57a3SBrian Somers     sep = strchr(sep, '.');
1124c11e57a3SBrian Somers     if (sep) {
1125c11e57a3SBrian Somers       if (strcasecmp(++sep, "random") == 0) {
1126565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1127565e35e5SBrian Somers 	randinit();
1128565e35e5SBrian Somers       } else {
1129c11e57a3SBrian Somers 	timeout = atoi(sep);
1130565e35e5SBrian Somers 	if (timeout >= 0)
1131565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1132565e35e5SBrian Somers 	else {
1133dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1134565e35e5SBrian Somers 	  return -1;
1135565e35e5SBrian Somers 	}
1136565e35e5SBrian Somers       }
1137565e35e5SBrian Somers     } else
1138565e35e5SBrian Somers       /* Default next timeout */
1139565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1140565e35e5SBrian Somers 
114125092092SBrian Somers     if (arg->argc == arg->argn+2) {
114225092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1143565e35e5SBrian Somers 
1144565e35e5SBrian Somers       if (tries >= 0) {
1145565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1146565e35e5SBrian Somers       } else {
1147dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1148565e35e5SBrian Somers 	return 1;
1149565e35e5SBrian Somers       }
1150565e35e5SBrian Somers     }
1151565e35e5SBrian Somers     return 0;
1152565e35e5SBrian Somers   }
1153c11e57a3SBrian Somers 
1154565e35e5SBrian Somers   return -1;
1155c7cc5030SBrian Somers }
1156c7cc5030SBrian Somers 
1157cdbbb6b5SBrian Somers static const char *states[] = {
1158565e35e5SBrian Somers   "closed",
1159565e35e5SBrian Somers   "opening",
1160565e35e5SBrian Somers   "hangup",
1161565e35e5SBrian Somers   "dial",
1162565e35e5SBrian Somers   "login",
1163565e35e5SBrian Somers   "ready",
1164565e35e5SBrian Somers   "lcp",
1165565e35e5SBrian Somers   "auth",
116692b09558SBrian Somers   "cbcp",
1167565e35e5SBrian Somers   "open"
1168c7cc5030SBrian Somers };
1169c7cc5030SBrian Somers 
1170643f4904SBrian Somers const char *
1171c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1172c7cc5030SBrian Somers {
1173c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1174c7cc5030SBrian Somers     return "unknown";
1175c7cc5030SBrian Somers   return states[dl->state];
1176c7cc5030SBrian Somers }
11776f384573SBrian Somers 
11789ae58882SBrian Somers static void
11799ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
11809ae58882SBrian Somers {
11819ae58882SBrian Somers   if (state != dl->state) {
11829ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
11839ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
11849ae58882SBrian Somers                  states[state]);
11859ae58882SBrian Somers       dl->state = state;
11869ae58882SBrian Somers     } else
11879ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
11889ae58882SBrian Somers   }
11899ae58882SBrian Somers }
11909ae58882SBrian Somers 
11916f384573SBrian Somers struct datalink *
119296c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
119396c9bb21SBrian Somers              int fd)
11946f384573SBrian Somers {
1195b7c5748eSBrian Somers   struct datalink *dl, *cdl;
1196479508cfSBrian Somers   struct fsm_retry copy;
1197b7c5748eSBrian Somers   char *oname;
11986f384573SBrian Somers 
119996c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
120096c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
12016f384573SBrian Somers 
120296c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
120396c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
120496c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
120596c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
12066f384573SBrian Somers   }
1207b7c5748eSBrian Somers 
1208b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1209b7c5748eSBrian Somers   oname = NULL;
1210b7c5748eSBrian Somers   do {
1211b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1212b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1213b7c5748eSBrian Somers         if (oname)
1214b7c5748eSBrian Somers           free(datalink_NextName(dl));
1215b7c5748eSBrian Somers         else
1216b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1217b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1218b7c5748eSBrian Somers       }
1219b7c5748eSBrian Somers   } while (cdl);
1220b7c5748eSBrian Somers 
1221b7c5748eSBrian Somers   if (oname) {
1222b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1223b7c5748eSBrian Somers     free(oname);
1224b7c5748eSBrian Somers   } else {
122596c9bb21SBrian Somers     dl->name = strdup(dl->name);
1226b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1227b7c5748eSBrian Somers   }
1228b7c5748eSBrian Somers   (*niov)++;
12296f384573SBrian Somers 
12306f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
12316f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
12326f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
12336f384573SBrian Somers   dl->desc.Read = datalink_Read;
12346f384573SBrian Somers   dl->desc.Write = datalink_Write;
12356f384573SBrian Somers 
12366f384573SBrian Somers   mp_linkInit(&dl->mp);
12376f384573SBrian Somers   *dl->phone.list = '\0';
12386f384573SBrian Somers   dl->phone.next = NULL;
12396f384573SBrian Somers   dl->phone.alt = NULL;
12406f384573SBrian Somers   dl->phone.chosen = "N/A";
12416f384573SBrian Somers 
12426f384573SBrian Somers   dl->bundle = bundle;
12436f384573SBrian Somers   dl->next = NULL;
1244c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
1245c11e57a3SBrian Somers   dl->dial.tries = 0;
12466f384573SBrian Somers   dl->reconnect_tries = 0;
12476f384573SBrian Somers   dl->parent = &bundle->fsm;
12486f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
12496f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
12506f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
12516f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
12526f384573SBrian Somers   dl->fsmp.object = dl;
12536f384573SBrian Somers 
125496c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
125596c9bb21SBrian Somers 
125696c9bb21SBrian Somers   if (!dl->physical) {
12576f384573SBrian Somers     free(dl->name);
12586f384573SBrian Somers     free(dl);
12596f384573SBrian Somers     dl = NULL;
12609ae58882SBrian Somers   } else {
1261479508cfSBrian Somers     copy = dl->pap.cfg.fsm;
1262f0cdd9c0SBrian Somers     pap_Init(&dl->pap, dl->physical);
1263479508cfSBrian Somers     dl->pap.cfg.fsm = copy;
1264f0cdd9c0SBrian Somers 
1265479508cfSBrian Somers     copy = dl->chap.auth.cfg.fsm;
1266f0cdd9c0SBrian Somers     chap_Init(&dl->chap, dl->physical);
1267479508cfSBrian Somers     dl->chap.auth.cfg.fsm = copy;
1268f0cdd9c0SBrian Somers 
126992b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
12706f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
12716f384573SBrian Somers 
12729ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
12739ae58882SBrian Somers               dl->name, datalink_State(dl));
12749ae58882SBrian Somers   }
12759ae58882SBrian Somers 
12766f384573SBrian Somers   return dl;
12776f384573SBrian Somers }
12786f384573SBrian Somers 
12796f384573SBrian Somers int
128085fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
128185fd273aSBrian Somers              pid_t newpid)
12826f384573SBrian Somers {
128396c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
128496c9bb21SBrian Somers   int link_fd;
12856f384573SBrian Somers 
128696c9bb21SBrian Somers   if (dl) {
1287c11e57a3SBrian Somers     timer_Stop(&dl->dial.timer);
128892b09558SBrian Somers     /* The following is purely for the sake of paranoia */
128992b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1290dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1291dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
12926f384573SBrian Somers   }
12936f384573SBrian Somers 
129496c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
129596c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
129696c9bb21SBrian Somers     if (dl) {
12976f384573SBrian Somers       free(dl->name);
12986f384573SBrian Somers       free(dl);
129996c9bb21SBrian Somers     }
130096c9bb21SBrian Somers     return -1;
130196c9bb21SBrian Somers   }
130296c9bb21SBrian Somers 
130396c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
130496c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
130596c9bb21SBrian Somers   iov[*niov].iov_base =
130696c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
130796c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
130896c9bb21SBrian Somers 
130985fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
131096c9bb21SBrian Somers 
131196c9bb21SBrian Somers   if (link_fd == -1 && dl) {
131296c9bb21SBrian Somers     free(dl->name);
131396c9bb21SBrian Somers     free(dl);
131496c9bb21SBrian Somers   }
13156f384573SBrian Somers 
13166f384573SBrian Somers   return link_fd;
13176f384573SBrian Somers }
13186f384573SBrian Somers 
131958d55334SBrian Somers void
132058d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
132158d55334SBrian Somers {
132258d55334SBrian Somers   free(dl->name);
132358d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
132458d55334SBrian Somers }
132558d55334SBrian Somers 
132684917b87SBrian Somers char *
132784917b87SBrian Somers datalink_NextName(struct datalink *dl)
13286f384573SBrian Somers {
13296f384573SBrian Somers   int f, n;
133084917b87SBrian Somers   char *name, *oname;
13316f384573SBrian Somers 
13326f384573SBrian Somers   n = strlen(dl->name);
13336f384573SBrian Somers   name = (char *)malloc(n+3);
13346f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
13356f384573SBrian Somers     if (!isdigit(dl->name[f]))
13366f384573SBrian Somers       break;
13376f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
13386f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
133984917b87SBrian Somers   oname = dl->name;
134054cd8e13SBrian Somers   dl->name = name;
134154cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
134284917b87SBrian Somers   return oname;
13436f384573SBrian Somers }
1344dd0645c5SBrian Somers 
1345dd0645c5SBrian Somers int
1346dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1347dd0645c5SBrian Somers {
1348dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1349dd0645c5SBrian Somers     return 0;
1350dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1351dd0645c5SBrian Somers     dl->script.run = 0;
1352dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1353dd0645c5SBrian Somers     dl->reconnect_tries = 0;
135481358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1355dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1356dd0645c5SBrian Somers   return 1;
1357dd0645c5SBrian Somers }
1358c11e57a3SBrian Somers 
1359c11e57a3SBrian Somers int
1360c11e57a3SBrian Somers datalink_GetDialTimeout(struct datalink *dl)
1361c11e57a3SBrian Somers {
1362c11e57a3SBrian Somers   int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
1363c11e57a3SBrian Somers 
1364c11e57a3SBrian Somers   if (dl->dial.incs < dl->cfg.dial.maxinc)
1365c11e57a3SBrian Somers     dl->dial.incs++;
1366c11e57a3SBrian Somers 
1367c11e57a3SBrian Somers   return result;
1368c11e57a3SBrian Somers }
1369