xref: /freebsd/usr.sbin/ppp/datalink.c (revision 5945a079)
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  *
265945a079SBrian Somers  *	$Id: datalink.c,v 1.35 1999/03/04 17:42:15 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);
14792b09558SBrian Somers   } else if (dl->bundle->CleaningUp ||
1486f384573SBrian Somers       (dl->physical->type == PHYS_DIRECT) ||
149c11e57a3SBrian Somers       ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
15081358fa3SBrian Somers        !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
1519ae58882SBrian Somers     datalink_NewState(dl, DATALINK_CLOSED);
152c11e57a3SBrian Somers     dl->dial.tries = -1;
153c11e57a3SBrian Somers     dl->dial.incs = 0;
1543006ec67SBrian Somers     dl->reconnect_tries = 0;
1553006ec67SBrian Somers     bundle_LinkClosed(dl->bundle, dl);
1563b0f8d2eSBrian Somers     if (!dl->bundle->CleaningUp)
157c11e57a3SBrian Somers       datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1583006ec67SBrian Somers   } else {
1599ae58882SBrian Somers     datalink_NewState(dl, DATALINK_OPENING);
160c11e57a3SBrian Somers     if (dl->dial.tries < 0) {
161565e35e5SBrian Somers       datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
162c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
163c11e57a3SBrian Somers       dl->dial.incs = 0;
164abff9baeSBrian Somers       dl->reconnect_tries--;
165abff9baeSBrian Somers     } else {
166f9545805SBrian Somers       if (dl->phone.next == NULL)
167c11e57a3SBrian Somers         datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
1683006ec67SBrian Somers       else
169565e35e5SBrian Somers         datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
1703006ec67SBrian Somers     }
171abff9baeSBrian Somers   }
172abff9baeSBrian Somers }
1733006ec67SBrian Somers 
174f9545805SBrian Somers static const char *
175f9545805SBrian Somers datalink_ChoosePhoneNumber(struct datalink *dl)
176f9545805SBrian Somers {
177f9545805SBrian Somers   char *phone;
178f9545805SBrian Somers 
179f9545805SBrian Somers   if (dl->phone.alt == NULL) {
180f9545805SBrian Somers     if (dl->phone.next == NULL) {
181f9545805SBrian Somers       strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
182f9545805SBrian Somers       dl->phone.list[sizeof dl->phone.list - 1] = '\0';
183f9545805SBrian Somers       dl->phone.next = dl->phone.list;
184f9545805SBrian Somers     }
185f9545805SBrian Somers     dl->phone.alt = strsep(&dl->phone.next, ":");
186f9545805SBrian Somers   }
187f9545805SBrian Somers   phone = strsep(&dl->phone.alt, "|");
188f9545805SBrian Somers   dl->phone.chosen = *phone ? phone : "[NONE]";
189f9545805SBrian Somers   if (*phone)
190dd7e2610SBrian Somers     log_Printf(LogPHASE, "Phone: %s\n", phone);
191f9545805SBrian Somers   return phone;
192f9545805SBrian Somers }
193f9545805SBrian Somers 
194c5a5a6caSBrian Somers static void
195c5a5a6caSBrian Somers datalink_LoginDone(struct datalink *dl)
196c5a5a6caSBrian Somers {
197d345321bSBrian Somers   if (!dl->script.packetmode) {
198c11e57a3SBrian Somers     dl->dial.tries = -1;
199c11e57a3SBrian Somers     dl->dial.incs = 0;
2009ae58882SBrian Somers     datalink_NewState(dl, DATALINK_READY);
201d345321bSBrian Somers   } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
202c11e57a3SBrian Somers     dl->dial.tries = 0;
203dd7e2610SBrian Somers     log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
204c5a5a6caSBrian Somers     if (dl->script.run) {
2059ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
206c5a5a6caSBrian Somers       modem_Offline(dl->physical);
207f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
208030e4ebbSBrian Somers     } else {
209d4af231cSBrian Somers       timer_Stop(&dl->physical->Timer);
210030e4ebbSBrian Somers       if (dl->physical->type == PHYS_DEDICATED)
211030e4ebbSBrian Somers         /* force a redial timeout */
212030e4ebbSBrian Somers         modem_Close(dl->physical);
213c5a5a6caSBrian Somers       datalink_HangupDone(dl);
214030e4ebbSBrian Somers     }
215c5a5a6caSBrian Somers   } else {
216c11e57a3SBrian Somers     dl->dial.tries = -1;
217c11e57a3SBrian Somers     dl->dial.incs = 0;
218c7cc5030SBrian Somers 
219643f4904SBrian Somers     hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
2203b0f8d2eSBrian Somers     async_Init(&dl->physical->async);
2213b0f8d2eSBrian Somers 
222cd9647a1SBrian Somers     lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
223cd9647a1SBrian Somers               0 : dl->physical->link.lcp.cfg.openmode);
2243b0f8d2eSBrian Somers     ccp_Setup(&dl->physical->link.ccp);
225503a7782SBrian Somers 
2269ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
227dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.lcp.fsm);
228dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.lcp.fsm);
229c5a5a6caSBrian Somers   }
230c5a5a6caSBrian Somers }
231c5a5a6caSBrian Somers 
2323006ec67SBrian Somers static int
2333006ec67SBrian Somers datalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
2343006ec67SBrian Somers                    int *n)
2353006ec67SBrian Somers {
2363006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
2373006ec67SBrian Somers   int result;
2383006ec67SBrian Somers 
239310c3babSBrian Somers   result = 0;
2403006ec67SBrian Somers   switch (dl->state) {
2413006ec67SBrian Somers     case DATALINK_CLOSED:
24281358fa3SBrian Somers       if ((dl->physical->type &
24381358fa3SBrian Somers            (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) &&
244565e35e5SBrian Somers           !bundle_IsDead(dl->bundle))
245565e35e5SBrian Somers         /*
24681358fa3SBrian Somers          * Our first time in - DEDICATED & DDIAL never come down, and
24781358fa3SBrian Somers          * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED.
24881358fa3SBrian Somers          * Go to DATALINK_OPENING via datalink_Up() and fall through.
249565e35e5SBrian Somers          */
250565e35e5SBrian Somers         datalink_Up(dl, 1, 1);
251565e35e5SBrian Somers       else
252310c3babSBrian Somers         break;
253565e35e5SBrian Somers       /* fall through */
2543006ec67SBrian Somers 
2553006ec67SBrian Somers     case DATALINK_OPENING:
256c11e57a3SBrian Somers       if (dl->dial.timer.state != TIMER_RUNNING) {
257c11e57a3SBrian Somers         if (--dl->dial.tries < 0)
258c11e57a3SBrian Somers           dl->dial.tries = 0;
2593006ec67SBrian Somers         if (modem_Open(dl->physical, dl->bundle) >= 0) {
260bf1d3ff6SBrian Somers           log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
261bf1d3ff6SBrian Somers                            "Type `~?' for help\r\n", dl->name,
262bf1d3ff6SBrian Somers                            dl->physical->name.full);
263c5a5a6caSBrian Somers           if (dl->script.run) {
2649ae58882SBrian Somers             datalink_NewState(dl, DATALINK_DIAL);
265f9545805SBrian Somers             chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
266f9545805SBrian Somers                       datalink_ChoosePhoneNumber(dl));
26781358fa3SBrian Somers             if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
268565e35e5SBrian Somers                 dl->cfg.dial.max)
269dd7e2610SBrian Somers               log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
270c11e57a3SBrian Somers                         dl->name, dl->cfg.dial.max - dl->dial.tries,
271565e35e5SBrian Somers                         dl->cfg.dial.max);
272c5a5a6caSBrian Somers           } else
273c5a5a6caSBrian Somers             datalink_LoginDone(dl);
2748b09cf1cSBrian Somers           return datalink_UpdateSet(d, r, w, e, n);
2753006ec67SBrian Somers         } else {
27681358fa3SBrian Somers           if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
277565e35e5SBrian Somers               dl->cfg.dial.max)
278dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
279c11e57a3SBrian Somers                        dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
2803006ec67SBrian Somers           else
281dd7e2610SBrian Somers             log_Printf(LogCHAT, "Failed to open modem\n");
2823006ec67SBrian Somers 
2833b0f8d2eSBrian Somers           if (dl->bundle->CleaningUp ||
28481358fa3SBrian Somers               (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
285c11e57a3SBrian Somers                dl->cfg.dial.max && dl->dial.tries == 0)) {
2869ae58882SBrian Somers             datalink_NewState(dl, DATALINK_CLOSED);
2873006ec67SBrian Somers             dl->reconnect_tries = 0;
288c11e57a3SBrian Somers             dl->dial.tries = -1;
289bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s\n",
290bf1d3ff6SBrian Somers                              dl->physical->name.full);
2915b8b8060SBrian Somers             bundle_LinkClosed(dl->bundle, dl);
292c5a5a6caSBrian Somers           }
293bf1d3ff6SBrian Somers           if (!dl->bundle->CleaningUp) {
294c11e57a3SBrian Somers             int timeout;
295c11e57a3SBrian Somers 
296c11e57a3SBrian Somers             timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
297bf1d3ff6SBrian Somers             log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
298b5c347a3SBrian Somers                              dl->physical->name.full, timeout);
2993006ec67SBrian Somers           }
3003006ec67SBrian Somers         }
301bf1d3ff6SBrian Somers       }
302310c3babSBrian Somers       break;
3033006ec67SBrian Somers 
3043006ec67SBrian Somers     case DATALINK_HANGUP:
3053006ec67SBrian Somers     case DATALINK_DIAL:
3063006ec67SBrian Somers     case DATALINK_LOGIN:
3073006ec67SBrian Somers       result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
3083006ec67SBrian Somers       switch (dl->chat.state) {
3093006ec67SBrian Somers         case CHAT_DONE:
3103006ec67SBrian Somers           /* script succeeded */
31139d94652SBrian Somers           chat_Destroy(&dl->chat);
3123006ec67SBrian Somers           switch(dl->state) {
3133006ec67SBrian Somers             case DATALINK_HANGUP:
3143006ec67SBrian Somers               datalink_HangupDone(dl);
3153006ec67SBrian Somers               break;
3163006ec67SBrian Somers             case DATALINK_DIAL:
3179ae58882SBrian Somers               datalink_NewState(dl, DATALINK_LOGIN);
318f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
3191342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3203006ec67SBrian Somers             case DATALINK_LOGIN:
32147dd77c1SBrian Somers               dl->phone.alt = NULL;
322c5a5a6caSBrian Somers               datalink_LoginDone(dl);
323b51a60ccSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3243006ec67SBrian Somers           }
3253006ec67SBrian Somers           break;
3263006ec67SBrian Somers         case CHAT_FAILED:
3273006ec67SBrian Somers           /* Going down - script failed */
328dd7e2610SBrian Somers           log_Printf(LogWARN, "Chat script failed\n");
32939d94652SBrian Somers           chat_Destroy(&dl->chat);
3303006ec67SBrian Somers           switch(dl->state) {
3313006ec67SBrian Somers             case DATALINK_HANGUP:
3323006ec67SBrian Somers               datalink_HangupDone(dl);
3333006ec67SBrian Somers               break;
3343006ec67SBrian Somers             case DATALINK_DIAL:
3353006ec67SBrian Somers             case DATALINK_LOGIN:
3369ae58882SBrian Somers               datalink_NewState(dl, DATALINK_HANGUP);
337d4af231cSBrian Somers               modem_Offline(dl->physical);	/* Is this required ? */
338f9545805SBrian Somers               chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
3391342caedSBrian Somers               return datalink_UpdateSet(d, r, w, e, n);
3403006ec67SBrian Somers           }
3413006ec67SBrian Somers           break;
3423006ec67SBrian Somers       }
3433006ec67SBrian Somers       break;
344c7cc5030SBrian Somers 
345c7cc5030SBrian Somers     case DATALINK_READY:
346e2ebb036SBrian Somers     case DATALINK_LCP:
347e2ebb036SBrian Somers     case DATALINK_AUTH:
34892b09558SBrian Somers     case DATALINK_CBCP:
3493006ec67SBrian Somers     case DATALINK_OPEN:
35058330d7bSBrian Somers       result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
35158330d7bSBrian Somers                descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
3523006ec67SBrian Somers       break;
3533006ec67SBrian Somers   }
3543006ec67SBrian Somers   return result;
3553006ec67SBrian Somers }
3563006ec67SBrian Somers 
357ea722969SBrian Somers int
358ea722969SBrian Somers datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
359ea722969SBrian Somers {
360ea722969SBrian Somers   return physical_RemoveFromSet(dl->physical, r, w, e);
361ea722969SBrian Somers }
362ea722969SBrian Somers 
3633006ec67SBrian Somers static int
3642f786681SBrian Somers datalink_IsSet(struct descriptor *d, const fd_set *fdset)
3653006ec67SBrian Somers {
3663006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
3673006ec67SBrian Somers 
3683006ec67SBrian Somers   switch (dl->state) {
3693006ec67SBrian Somers     case DATALINK_CLOSED:
370c7cc5030SBrian Somers     case DATALINK_OPENING:
3713006ec67SBrian Somers       break;
372c7cc5030SBrian Somers 
3733006ec67SBrian Somers     case DATALINK_HANGUP:
3743006ec67SBrian Somers     case DATALINK_DIAL:
3753006ec67SBrian Somers     case DATALINK_LOGIN:
3763006ec67SBrian Somers       return descriptor_IsSet(&dl->chat.desc, fdset);
377c7cc5030SBrian Somers 
378c7cc5030SBrian Somers     case DATALINK_READY:
379e2ebb036SBrian Somers     case DATALINK_LCP:
380e2ebb036SBrian Somers     case DATALINK_AUTH:
38192b09558SBrian Somers     case DATALINK_CBCP:
3823006ec67SBrian Somers     case DATALINK_OPEN:
38358330d7bSBrian Somers       return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
38458330d7bSBrian Somers              descriptor_IsSet(&dl->physical->desc, fdset);
3853006ec67SBrian Somers   }
3863006ec67SBrian Somers   return 0;
3873006ec67SBrian Somers }
3883006ec67SBrian Somers 
3893006ec67SBrian Somers static void
3903006ec67SBrian Somers datalink_Read(struct descriptor *d, struct bundle *bundle, 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       descriptor_Read(&dl->chat.desc, bundle, fdset);
4033006ec67SBrian Somers       break;
404c7cc5030SBrian Somers 
405c7cc5030SBrian Somers     case DATALINK_READY:
406e2ebb036SBrian Somers     case DATALINK_LCP:
407e2ebb036SBrian Somers     case DATALINK_AUTH:
40892b09558SBrian Somers     case DATALINK_CBCP:
4093006ec67SBrian Somers     case DATALINK_OPEN:
41058330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
41158330d7bSBrian Somers         descriptor_Read(&dl->chap.desc, bundle, fdset);
41258330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
4133006ec67SBrian Somers         descriptor_Read(&dl->physical->desc, bundle, fdset);
4143006ec67SBrian Somers       break;
4153006ec67SBrian Somers   }
4163006ec67SBrian Somers }
4173006ec67SBrian Somers 
4181af29a6eSBrian Somers static int
419f4768038SBrian Somers datalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
4203006ec67SBrian Somers {
4213006ec67SBrian Somers   struct datalink *dl = descriptor2datalink(d);
4221af29a6eSBrian Somers   int result = 0;
4233006ec67SBrian Somers 
4243006ec67SBrian Somers   switch (dl->state) {
4253006ec67SBrian Somers     case DATALINK_CLOSED:
426c7cc5030SBrian Somers     case DATALINK_OPENING:
4273006ec67SBrian Somers       break;
428c7cc5030SBrian Somers 
4293006ec67SBrian Somers     case DATALINK_HANGUP:
4303006ec67SBrian Somers     case DATALINK_DIAL:
4313006ec67SBrian Somers     case DATALINK_LOGIN:
4321af29a6eSBrian Somers       result = descriptor_Write(&dl->chat.desc, bundle, fdset);
4333006ec67SBrian Somers       break;
434c7cc5030SBrian Somers 
435c7cc5030SBrian Somers     case DATALINK_READY:
436e2ebb036SBrian Somers     case DATALINK_LCP:
437e2ebb036SBrian Somers     case DATALINK_AUTH:
43892b09558SBrian Somers     case DATALINK_CBCP:
4393006ec67SBrian Somers     case DATALINK_OPEN:
44058330d7bSBrian Somers       if (descriptor_IsSet(&dl->chap.desc, fdset))
44158330d7bSBrian Somers         result += descriptor_Write(&dl->chap.desc, bundle, fdset);
44258330d7bSBrian Somers       if (descriptor_IsSet(&dl->physical->desc, fdset))
44358330d7bSBrian Somers         result += descriptor_Write(&dl->physical->desc, bundle, fdset);
4443006ec67SBrian Somers       break;
4453006ec67SBrian Somers   }
4461af29a6eSBrian Somers 
4471af29a6eSBrian Somers   return result;
4483006ec67SBrian Somers }
4493006ec67SBrian Somers 
4506d666775SBrian Somers static void
4519c81b87dSBrian Somers datalink_ComeDown(struct datalink *dl, int how)
452e2ebb036SBrian Somers {
4539c81b87dSBrian Somers   if (how != CLOSE_NORMAL) {
454c11e57a3SBrian Somers     dl->dial.tries = -1;
455e2ebb036SBrian Somers     dl->reconnect_tries = 0;
4567729a182SBrian Somers     if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
4579c81b87dSBrian Somers       dl->stayonline = 1;
458e2ebb036SBrian Somers   }
459e2ebb036SBrian Somers 
4607729a182SBrian Somers   if (dl->state >= DATALINK_READY && dl->stayonline) {
4619c81b87dSBrian Somers     dl->stayonline = 0;
462d4af231cSBrian Somers     timer_Stop(&dl->physical->Timer);
4639c81b87dSBrian Somers     datalink_NewState(dl, DATALINK_READY);
4649c81b87dSBrian Somers   } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
465e2ebb036SBrian Somers     modem_Offline(dl->physical);
466b6f5f442SBrian Somers     chat_Destroy(&dl->chat);
467e2ebb036SBrian Somers     if (dl->script.run && dl->state != DATALINK_OPENING) {
4689ae58882SBrian Somers       datalink_NewState(dl, DATALINK_HANGUP);
469f9545805SBrian Somers       chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
470e2ebb036SBrian Somers     } else
471e2ebb036SBrian Somers       datalink_HangupDone(dl);
472e2ebb036SBrian Somers   }
473e2ebb036SBrian Somers }
474e2ebb036SBrian Somers 
475e2ebb036SBrian Somers static void
4766d666775SBrian Somers datalink_LayerStart(void *v, struct fsm *fp)
4776d666775SBrian Somers {
4786d666775SBrian Somers   /* The given FSM is about to start up ! */
4796d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
480a611cad6SBrian Somers 
48192e872e7SBrian Somers   if (fp->proto == PROTO_LCP)
482e2ebb036SBrian Somers     (*dl->parent->LayerStart)(dl->parent->object, fp);
483e2ebb036SBrian Somers }
4846d666775SBrian Somers 
4856d666775SBrian Somers static void
4866d666775SBrian Somers datalink_LayerUp(void *v, struct fsm *fp)
4876d666775SBrian Somers {
4886d666775SBrian Somers   /* The given fsm is now up */
4896d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
4905e315498SBrian Somers   struct lcp *lcp = &dl->physical->link.lcp;
4916d666775SBrian Somers 
49292e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
493f0cdd9c0SBrian Somers     datalink_GotAuthname(dl, "");
4945e315498SBrian Somers     lcp->auth_ineed = lcp->want_auth;
4955e315498SBrian Somers     lcp->auth_iwait = lcp->his_auth;
4965e315498SBrian Somers     if (lcp->his_auth || lcp->want_auth) {
4975945a079SBrian Somers       if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
4985563ebdeSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
499dd7e2610SBrian Somers       log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
5005e315498SBrian Somers                 Auth2Nam(lcp->his_auth, lcp->his_authtype),
5015e315498SBrian Somers                 Auth2Nam(lcp->want_auth, lcp->want_authtype));
5025e315498SBrian Somers       if (lcp->his_auth == PROTO_PAP)
503f0cdd9c0SBrian Somers         auth_StartReq(&dl->pap);
5045e315498SBrian Somers       if (lcp->want_auth == PROTO_CHAP)
505f0cdd9c0SBrian Somers         auth_StartReq(&dl->chap.auth);
506e2ebb036SBrian Somers     } else
507e2ebb036SBrian Somers       datalink_AuthOk(dl);
508e2ebb036SBrian Somers   }
509e2ebb036SBrian Somers }
510e2ebb036SBrian Somers 
51164cfdfc6SBrian Somers static void
51264cfdfc6SBrian Somers datalink_AuthReInit(struct datalink *dl)
51364cfdfc6SBrian Somers {
51464cfdfc6SBrian Somers   auth_StopTimer(&dl->pap);
51564cfdfc6SBrian Somers   auth_StopTimer(&dl->chap.auth);
51664cfdfc6SBrian Somers   chap_ReInit(&dl->chap);
51764cfdfc6SBrian Somers }
51864cfdfc6SBrian Somers 
519e2ebb036SBrian Somers void
520f0cdd9c0SBrian Somers datalink_GotAuthname(struct datalink *dl, const char *name)
521643f4904SBrian Somers {
522f0cdd9c0SBrian Somers   strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
523f0cdd9c0SBrian Somers   dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
524643f4904SBrian Somers }
525643f4904SBrian Somers 
526643f4904SBrian Somers void
52792b09558SBrian Somers datalink_NCPUp(struct datalink *dl)
528e2ebb036SBrian Somers {
52906337856SBrian Somers   int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
5301df0a3b9SBrian Somers 
531dbf60d74SBrian Somers   if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
5321fa665f5SBrian Somers     /* we've authenticated in multilink mode ! */
5331fa665f5SBrian Somers     switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
5341fa665f5SBrian Somers       case MP_LINKSENT:
535ea722969SBrian Somers         /* We've handed the link off to another ppp (well, we will soon) ! */
5361fa665f5SBrian Somers         return;
5371fa665f5SBrian Somers       case MP_UP:
5380a1b5c9dSBrian Somers         /* First link in the bundle */
53992b09558SBrian Somers         auth_Select(dl->bundle, dl->peer.authname);
5400a1b5c9dSBrian Somers         /* fall through */
5411fa665f5SBrian Somers       case MP_ADDED:
5420a1b5c9dSBrian Somers         /* We're in multilink mode ! */
5431df0a3b9SBrian Somers         dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE;	/* override */
5441fa665f5SBrian Somers         break;
5451fa665f5SBrian Somers       case MP_FAILED:
54649052c95SBrian Somers         datalink_AuthNotOk(dl);
54749052c95SBrian Somers         return;
54849052c95SBrian Somers     }
54949052c95SBrian Somers   } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
550dd7e2610SBrian Somers     log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
551897f9429SBrian Somers     datalink_NewState(dl, DATALINK_OPEN);
552897f9429SBrian Somers     (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
55349052c95SBrian Somers     return;
55450abd4c8SBrian Somers   } else {
55550abd4c8SBrian Somers     dl->bundle->ncp.mp.peer = dl->peer;
556ce828a6eSBrian Somers     ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link);
55792b09558SBrian Somers     auth_Select(dl->bundle, dl->peer.authname);
55850abd4c8SBrian Somers   }
55949052c95SBrian Somers 
56006337856SBrian Somers   if (ccpok) {
561dd7e2610SBrian Somers     fsm_Up(&dl->physical->link.ccp.fsm);
562dd7e2610SBrian Somers     fsm_Open(&dl->physical->link.ccp.fsm);
56306337856SBrian Somers   }
5649ae58882SBrian Somers   datalink_NewState(dl, DATALINK_OPEN);
5653b0f8d2eSBrian Somers   bundle_NewPhase(dl->bundle, PHASE_NETWORK);
5663b0f8d2eSBrian Somers   (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
5676d666775SBrian Somers }
568e2ebb036SBrian Somers 
569e2ebb036SBrian Somers void
57092b09558SBrian Somers datalink_CBCPComplete(struct datalink *dl)
57192b09558SBrian Somers {
57292b09558SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
57364cfdfc6SBrian Somers   datalink_AuthReInit(dl);
57492b09558SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
57592b09558SBrian Somers }
57692b09558SBrian Somers 
57792b09558SBrian Somers void
57892b09558SBrian Somers datalink_CBCPFailed(struct datalink *dl)
57992b09558SBrian Somers {
58092b09558SBrian Somers   cbcp_Down(&dl->cbcp);
58192b09558SBrian Somers   datalink_CBCPComplete(dl);
58292b09558SBrian Somers }
58392b09558SBrian Somers 
58492b09558SBrian Somers void
58592b09558SBrian Somers datalink_AuthOk(struct datalink *dl)
58692b09558SBrian Somers {
5875165af6fSBrian Somers   if ((dl->physical->link.lcp.his_callback.opmask &
58892b09558SBrian Somers        CALLBACK_BIT(CALLBACK_CBCP) ||
5895165af6fSBrian Somers        dl->physical->link.lcp.want_callback.opmask &
5905165af6fSBrian Somers        CALLBACK_BIT(CALLBACK_CBCP)) &&
5915165af6fSBrian Somers       !(dl->physical->link.lcp.want_callback.opmask &
5925165af6fSBrian Somers         CALLBACK_BIT(CALLBACK_AUTH))) {
5935165af6fSBrian Somers     /* We must have agreed CBCP if AUTH isn't there any more */
59492b09558SBrian Somers     datalink_NewState(dl, DATALINK_CBCP);
59592b09558SBrian Somers     cbcp_Up(&dl->cbcp);
59692b09558SBrian Somers   } else if (dl->physical->link.lcp.want_callback.opmask) {
5975165af6fSBrian Somers     /* It's not CBCP */
59892b09558SBrian Somers     log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
59992b09558SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
60064cfdfc6SBrian Somers     datalink_AuthReInit(dl);
60192b09558SBrian Somers     fsm_Close(&dl->physical->link.lcp.fsm);
60292b09558SBrian Somers   } else
60392b09558SBrian Somers     switch (dl->physical->link.lcp.his_callback.opmask) {
60492b09558SBrian Somers       case 0:
60592b09558SBrian Somers         datalink_NCPUp(dl);
60692b09558SBrian Somers         break;
60792b09558SBrian Somers 
60892b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_AUTH):
60992b09558SBrian Somers         auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
61092b09558SBrian Somers                           sizeof dl->cbcp.fsm.phone);
61192b09558SBrian Somers         if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
61292b09558SBrian Somers           log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
61392b09558SBrian Somers                      dl->peer.authname);
61492b09558SBrian Somers           *dl->cbcp.fsm.phone = '\0';
61592b09558SBrian Somers         } else {
61692b09558SBrian Somers           char *ptr = strchr(dl->cbcp.fsm.phone, ',');
61792b09558SBrian Somers           if (ptr)
61892b09558SBrian Somers             *ptr = '\0';	/* Call back on the first number */
61992b09558SBrian Somers           log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
62092b09558SBrian Somers                      dl->cbcp.fsm.phone);
62192b09558SBrian Somers           dl->cbcp.required = 1;
62292b09558SBrian Somers         }
62392b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
62492b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
62564cfdfc6SBrian Somers         datalink_AuthReInit(dl);
62692b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
62792b09558SBrian Somers         break;
62892b09558SBrian Somers 
62992b09558SBrian Somers       case CALLBACK_BIT(CALLBACK_E164):
63092b09558SBrian Somers         strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
63192b09558SBrian Somers                 sizeof dl->cbcp.fsm.phone - 1);
63292b09558SBrian Somers         dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
63392b09558SBrian Somers         log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
63492b09558SBrian Somers                    dl->cbcp.fsm.phone);
63592b09558SBrian Somers         dl->cbcp.required = 1;
63692b09558SBrian Somers         dl->cbcp.fsm.delay = 0;
63792b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
63864cfdfc6SBrian Somers         datalink_AuthReInit(dl);
63992b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
64092b09558SBrian Somers         break;
64192b09558SBrian Somers 
64292b09558SBrian Somers       default:
64392b09558SBrian Somers         log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
64492b09558SBrian Somers                    dl->name);
64592b09558SBrian Somers         datalink_NewState(dl, DATALINK_LCP);
64664cfdfc6SBrian Somers         datalink_AuthReInit(dl);
64792b09558SBrian Somers         fsm_Close(&dl->physical->link.lcp.fsm);
64892b09558SBrian Somers         break;
64992b09558SBrian Somers     }
65092b09558SBrian Somers }
65192b09558SBrian Somers 
65292b09558SBrian Somers void
653e2ebb036SBrian Somers datalink_AuthNotOk(struct datalink *dl)
654e2ebb036SBrian Somers {
6559ae58882SBrian Somers   datalink_NewState(dl, DATALINK_LCP);
65664cfdfc6SBrian Somers   datalink_AuthReInit(dl);
657dd7e2610SBrian Somers   fsm_Close(&dl->physical->link.lcp.fsm);
6586d666775SBrian Somers }
6596d666775SBrian Somers 
6606d666775SBrian Somers static void
6616d666775SBrian Somers datalink_LayerDown(void *v, struct fsm *fp)
6626d666775SBrian Somers {
6636d666775SBrian Somers   /* The given FSM has been told to come down */
6646d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
665a611cad6SBrian Somers 
66692e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
667e2ebb036SBrian Somers     switch (dl->state) {
668e2ebb036SBrian Somers       case DATALINK_OPEN:
669643f4904SBrian Somers         peerid_Init(&dl->peer);
67009206a6fSBrian Somers         fsm2initial(&dl->physical->link.ccp.fsm);
671ff0f9439SBrian Somers         datalink_NewState(dl, DATALINK_LCP);  /* before parent TLD */
672e2ebb036SBrian Somers         (*dl->parent->LayerDown)(dl->parent->object, fp);
67392b09558SBrian Somers         /* fall through (just in case) */
67492b09558SBrian Somers 
67592b09558SBrian Somers       case DATALINK_CBCP:
67692b09558SBrian Somers         if (!dl->cbcp.required)
67792b09558SBrian Somers           cbcp_Down(&dl->cbcp);
67892b09558SBrian Somers         /* fall through (just in case) */
679e2ebb036SBrian Somers 
680e2ebb036SBrian Somers       case DATALINK_AUTH:
681dd7e2610SBrian Somers         timer_Stop(&dl->pap.authtimer);
682dd7e2610SBrian Somers         timer_Stop(&dl->chap.auth.authtimer);
6836d666775SBrian Somers     }
6849ae58882SBrian Somers     datalink_NewState(dl, DATALINK_LCP);
68564cfdfc6SBrian Somers     datalink_AuthReInit(dl);
686e2ebb036SBrian Somers   }
6876d666775SBrian Somers }
6886d666775SBrian Somers 
6896d666775SBrian Somers static void
6906d666775SBrian Somers datalink_LayerFinish(void *v, struct fsm *fp)
6916d666775SBrian Somers {
6926d666775SBrian Somers   /* The given fsm is now down */
6936d666775SBrian Somers   struct datalink *dl = (struct datalink *)v;
6946d666775SBrian Somers 
69592e872e7SBrian Somers   if (fp->proto == PROTO_LCP) {
69609206a6fSBrian Somers     fsm2initial(fp);
6976d666775SBrian Somers     (*dl->parent->LayerFinish)(dl->parent->object, fp);
6989c81b87dSBrian Somers     datalink_ComeDown(dl, CLOSE_NORMAL);
6990a1b5c9dSBrian Somers   } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
7000a1b5c9dSBrian Somers     fsm_Open(fp);		/* CCP goes to ST_STOPPED */
7016d666775SBrian Somers }
7026d666775SBrian Somers 
7033006ec67SBrian Somers struct datalink *
7046f384573SBrian Somers datalink_Create(const char *name, struct bundle *bundle, int type)
7053006ec67SBrian Somers {
7063006ec67SBrian Somers   struct datalink *dl;
7073006ec67SBrian Somers 
7083006ec67SBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
7093006ec67SBrian Somers   if (dl == NULL)
7103006ec67SBrian Somers     return dl;
7113006ec67SBrian Somers 
7123006ec67SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
7133006ec67SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
7143006ec67SBrian Somers   dl->desc.IsSet = datalink_IsSet;
7153006ec67SBrian Somers   dl->desc.Read = datalink_Read;
7163006ec67SBrian Somers   dl->desc.Write = datalink_Write;
7175b8b8060SBrian Somers 
718e718d1d7SBrian Somers   dl->state = DATALINK_CLOSED;
719e718d1d7SBrian Somers 
72073a13b5cSBrian Somers   *dl->cfg.script.dial = '\0';
72173a13b5cSBrian Somers   *dl->cfg.script.login = '\0';
72273a13b5cSBrian Somers   *dl->cfg.script.hangup = '\0';
723f9545805SBrian Somers   *dl->cfg.phone.list = '\0';
724f9545805SBrian Somers   *dl->phone.list = '\0';
725f9545805SBrian Somers   dl->phone.next = NULL;
726f9545805SBrian Somers   dl->phone.alt = NULL;
727f9545805SBrian Somers   dl->phone.chosen = "N/A";
7289c81b87dSBrian Somers   dl->stayonline = 0;
729c7cc5030SBrian Somers   dl->script.run = 1;
730c7cc5030SBrian Somers   dl->script.packetmode = 1;
7313b0f8d2eSBrian Somers   mp_linkInit(&dl->mp);
7325b8b8060SBrian Somers 
7333006ec67SBrian Somers   dl->bundle = bundle;
7343006ec67SBrian Somers   dl->next = NULL;
735e718d1d7SBrian Somers 
736c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
737e718d1d7SBrian Somers 
738c11e57a3SBrian Somers   dl->dial.tries = 0;
739565e35e5SBrian Somers   dl->cfg.dial.max = 1;
740565e35e5SBrian Somers   dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
741565e35e5SBrian Somers   dl->cfg.dial.timeout = DIAL_TIMEOUT;
742c11e57a3SBrian Somers   dl->cfg.dial.inc = 0;
743c11e57a3SBrian Somers   dl->cfg.dial.maxinc = 10;
744e718d1d7SBrian Somers 
745abff9baeSBrian Somers   dl->reconnect_tries = 0;
746565e35e5SBrian Somers   dl->cfg.reconnect.max = 0;
747565e35e5SBrian Somers   dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
748abff9baeSBrian Somers 
74992b09558SBrian Somers   dl->cfg.callback.opmask = 0;
75092b09558SBrian Somers   dl->cfg.cbcp.delay = 0;
75192b09558SBrian Somers   *dl->cfg.cbcp.phone = '\0';
75292b09558SBrian Somers   dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
75392b09558SBrian Somers 
7543006ec67SBrian Somers   dl->name = strdup(name);
755643f4904SBrian Somers   peerid_Init(&dl->peer);
7566f384573SBrian Somers   dl->parent = &bundle->fsm;
7573b0f8d2eSBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
7583b0f8d2eSBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
7593b0f8d2eSBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
7603b0f8d2eSBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
7613b0f8d2eSBrian Somers   dl->fsmp.object = dl;
7623b0f8d2eSBrian Somers 
763565e35e5SBrian Somers   if ((dl->physical = modem_Create(dl, type)) == NULL) {
7643006ec67SBrian Somers     free(dl->name);
7653006ec67SBrian Somers     free(dl);
7663006ec67SBrian Somers     return NULL;
7673006ec67SBrian Somers   }
768f0cdd9c0SBrian Somers 
769f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
770f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
77192b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
772f9545805SBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
7733006ec67SBrian Somers 
7749ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Created in %s state\n",
7759ae58882SBrian Somers              dl->name, datalink_State(dl));
7763006ec67SBrian Somers 
7773006ec67SBrian Somers   return dl;
7783006ec67SBrian Somers }
7793006ec67SBrian Somers 
7803006ec67SBrian Somers struct datalink *
781cd7bd93aSBrian Somers datalink_Clone(struct datalink *odl, const char *name)
782cd7bd93aSBrian Somers {
783cd7bd93aSBrian Somers   struct datalink *dl;
784cd7bd93aSBrian Somers 
785cd7bd93aSBrian Somers   dl = (struct datalink *)malloc(sizeof(struct datalink));
786cd7bd93aSBrian Somers   if (dl == NULL)
787cd7bd93aSBrian Somers     return dl;
788cd7bd93aSBrian Somers 
789cd7bd93aSBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
790cd7bd93aSBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
791cd7bd93aSBrian Somers   dl->desc.IsSet = datalink_IsSet;
792cd7bd93aSBrian Somers   dl->desc.Read = datalink_Read;
793cd7bd93aSBrian Somers   dl->desc.Write = datalink_Write;
794cd7bd93aSBrian Somers 
795cd7bd93aSBrian Somers   dl->state = DATALINK_CLOSED;
796cd7bd93aSBrian Somers 
797cd7bd93aSBrian Somers   memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
798cd7bd93aSBrian Somers   mp_linkInit(&dl->mp);
799cd7bd93aSBrian Somers   *dl->phone.list = '\0';
800643f4904SBrian Somers   dl->phone.next = NULL;
801643f4904SBrian Somers   dl->phone.alt = NULL;
802643f4904SBrian Somers   dl->phone.chosen = "N/A";
803cd7bd93aSBrian Somers   dl->bundle = odl->bundle;
804cd7bd93aSBrian Somers   dl->next = NULL;
805c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
806c11e57a3SBrian Somers   dl->dial.tries = 0;
807cd7bd93aSBrian Somers   dl->reconnect_tries = 0;
808cd7bd93aSBrian Somers   dl->name = strdup(name);
809643f4904SBrian Somers   peerid_Init(&dl->peer);
810cd7bd93aSBrian Somers   dl->parent = odl->parent;
811cd7bd93aSBrian Somers   memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
812643f4904SBrian Somers   dl->fsmp.object = dl;
813cd7bd93aSBrian Somers 
81481358fa3SBrian Somers   if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) {
815cd7bd93aSBrian Somers     free(dl->name);
816cd7bd93aSBrian Somers     free(dl);
817cd7bd93aSBrian Somers     return NULL;
818cd7bd93aSBrian Somers   }
819f0cdd9c0SBrian Somers   pap_Init(&dl->pap, dl->physical);
820479508cfSBrian Somers   dl->pap.cfg = odl->pap.cfg;
821f0cdd9c0SBrian Somers 
822f0cdd9c0SBrian Somers   chap_Init(&dl->chap, dl->physical);
823479508cfSBrian Somers   dl->chap.auth.cfg = odl->chap.auth.cfg;
824f0cdd9c0SBrian Somers 
825cd7bd93aSBrian Somers   memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
826cd7bd93aSBrian Somers   memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
827cd7bd93aSBrian Somers          sizeof dl->physical->link.lcp.cfg);
828cd7bd93aSBrian Somers   memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
829cd7bd93aSBrian Somers          sizeof dl->physical->link.ccp.cfg);
830cd7bd93aSBrian Somers   memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
831cd7bd93aSBrian Somers          sizeof dl->physical->async.cfg);
832cd7bd93aSBrian Somers 
83392b09558SBrian Somers   cbcp_Init(&dl->cbcp, dl->physical);
834cd7bd93aSBrian Somers   chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
835cd7bd93aSBrian Somers 
8369ae58882SBrian Somers   log_Printf(LogPHASE, "%s: Cloned in %s state\n",
8379ae58882SBrian Somers              dl->name, datalink_State(dl));
838cd7bd93aSBrian Somers 
839cd7bd93aSBrian Somers   return dl;
840cd7bd93aSBrian Somers }
841cd7bd93aSBrian Somers 
842cd7bd93aSBrian Somers struct datalink *
8433006ec67SBrian Somers datalink_Destroy(struct datalink *dl)
8443006ec67SBrian Somers {
8453006ec67SBrian Somers   struct datalink *result;
8463006ec67SBrian Somers 
84739d94652SBrian Somers   if (dl->state != DATALINK_CLOSED) {
848dd7e2610SBrian Somers     log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
849c7cc5030SBrian Somers               datalink_State(dl));
85039d94652SBrian Somers     switch (dl->state) {
85139d94652SBrian Somers       case DATALINK_HANGUP:
85239d94652SBrian Somers       case DATALINK_DIAL:
85339d94652SBrian Somers       case DATALINK_LOGIN:
85439d94652SBrian Somers         chat_Destroy(&dl->chat);	/* Gotta blat the timers ! */
85539d94652SBrian Somers         break;
85639d94652SBrian Somers     }
85739d94652SBrian Somers   }
8583006ec67SBrian Somers 
859c11e57a3SBrian Somers   timer_Stop(&dl->dial.timer);
8603006ec67SBrian Somers   result = dl->next;
8613b0f8d2eSBrian Somers   modem_Destroy(dl->physical);
8623006ec67SBrian Somers   free(dl->name);
8633006ec67SBrian Somers   free(dl);
8643006ec67SBrian Somers 
8653006ec67SBrian Somers   return result;
8663006ec67SBrian Somers }
8673006ec67SBrian Somers 
8683006ec67SBrian Somers void
869c5a5a6caSBrian Somers datalink_Up(struct datalink *dl, int runscripts, int packetmode)
8703006ec67SBrian Somers {
8716f384573SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
872565e35e5SBrian Somers     /* Ignore scripts */
873565e35e5SBrian Somers     runscripts = 0;
874565e35e5SBrian Somers 
875c7cc5030SBrian Somers   switch (dl->state) {
876c7cc5030SBrian Somers     case DATALINK_CLOSED:
8773b0f8d2eSBrian Somers       if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
8783b0f8d2eSBrian Somers           bundle_Phase(dl->bundle) == PHASE_TERMINATE)
8793b0f8d2eSBrian Somers         bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
8809ae58882SBrian Somers       datalink_NewState(dl, DATALINK_OPENING);
881565e35e5SBrian Somers       dl->reconnect_tries =
8826f384573SBrian Somers         dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
883c11e57a3SBrian Somers       dl->dial.tries = dl->cfg.dial.max;
884c5a5a6caSBrian Somers       dl->script.run = runscripts;
885c5a5a6caSBrian Somers       dl->script.packetmode = packetmode;
886c7cc5030SBrian Somers       break;
887c7cc5030SBrian Somers 
888c7cc5030SBrian Somers     case DATALINK_OPENING:
889c7cc5030SBrian Somers       if (!dl->script.run && runscripts)
890c7cc5030SBrian Somers         dl->script.run = 1;
891c7cc5030SBrian Somers       /* fall through */
892c7cc5030SBrian Somers 
893c7cc5030SBrian Somers     case DATALINK_DIAL:
894c7cc5030SBrian Somers     case DATALINK_LOGIN:
895c7cc5030SBrian Somers     case DATALINK_READY:
896c7cc5030SBrian Somers       if (!dl->script.packetmode && packetmode) {
897c7cc5030SBrian Somers         dl->script.packetmode = 1;
898c7cc5030SBrian Somers         if (dl->state == DATALINK_READY)
899c7cc5030SBrian Somers           datalink_LoginDone(dl);
900c7cc5030SBrian Somers       }
901c7cc5030SBrian Somers       break;
9023006ec67SBrian Somers   }
9033006ec67SBrian Somers }
9043006ec67SBrian Somers 
9053006ec67SBrian Somers void
9069c81b87dSBrian Somers datalink_Close(struct datalink *dl, int how)
907c7cc5030SBrian Somers {
908c7cc5030SBrian Somers   /* Please close */
909e2ebb036SBrian Somers   switch (dl->state) {
910e2ebb036SBrian Somers     case DATALINK_OPEN:
911643f4904SBrian Somers       peerid_Init(&dl->peer);
91209206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
913e2ebb036SBrian Somers       /* fall through */
914e2ebb036SBrian Somers 
91592b09558SBrian Somers     case DATALINK_CBCP:
916e2ebb036SBrian Somers     case DATALINK_AUTH:
917e2ebb036SBrian Somers     case DATALINK_LCP:
91864cfdfc6SBrian Somers       datalink_AuthReInit(dl);
919dd7e2610SBrian Somers       fsm_Close(&dl->physical->link.lcp.fsm);
9209c81b87dSBrian Somers       if (how != CLOSE_NORMAL) {
921c11e57a3SBrian Somers         dl->dial.tries = -1;
922c7cc5030SBrian Somers         dl->reconnect_tries = 0;
9239c81b87dSBrian Somers         if (how == CLOSE_LCP)
9249c81b87dSBrian Somers           dl->stayonline = 1;
925c7cc5030SBrian Somers       }
926e2ebb036SBrian Somers       break;
927e2ebb036SBrian Somers 
928e2ebb036SBrian Somers     default:
9299c81b87dSBrian Somers       datalink_ComeDown(dl, how);
930c7cc5030SBrian Somers   }
931e2ebb036SBrian Somers }
932c7cc5030SBrian Somers 
933c7cc5030SBrian Somers void
9349c81b87dSBrian Somers datalink_Down(struct datalink *dl, int how)
935c7cc5030SBrian Somers {
936c7cc5030SBrian Somers   /* Carrier is lost */
937e2ebb036SBrian Somers   switch (dl->state) {
938e2ebb036SBrian Somers     case DATALINK_OPEN:
939643f4904SBrian Somers       peerid_Init(&dl->peer);
94009206a6fSBrian Somers       fsm2initial(&dl->physical->link.ccp.fsm);
941e2ebb036SBrian Somers       /* fall through */
942e2ebb036SBrian Somers 
94392b09558SBrian Somers     case DATALINK_CBCP:
944e2ebb036SBrian Somers     case DATALINK_AUTH:
945e2ebb036SBrian Somers     case DATALINK_LCP:
94609206a6fSBrian Somers       fsm2initial(&dl->physical->link.lcp.fsm);
947e2ebb036SBrian Somers       /* fall through */
948c7cc5030SBrian Somers 
949e2ebb036SBrian Somers     default:
9509c81b87dSBrian Somers       datalink_ComeDown(dl, how);
951c7cc5030SBrian Somers   }
952e2ebb036SBrian Somers }
953c7cc5030SBrian Somers 
954c7cc5030SBrian Somers void
9553006ec67SBrian Somers datalink_StayDown(struct datalink *dl)
9563006ec67SBrian Somers {
9573006ec67SBrian Somers   dl->reconnect_tries = 0;
9583006ec67SBrian Somers }
959c7cc5030SBrian Somers 
9609c81b87dSBrian Somers void
9619c81b87dSBrian Somers datalink_DontHangup(struct datalink *dl)
9629c81b87dSBrian Somers {
9637729a182SBrian Somers   if (dl->state >= DATALINK_LCP)
9649c81b87dSBrian Somers     dl->stayonline = 1;
9659c81b87dSBrian Somers }
9669c81b87dSBrian Somers 
967643f4904SBrian Somers int
968643f4904SBrian Somers datalink_Show(struct cmdargs const *arg)
969c7cc5030SBrian Somers {
970643f4904SBrian Somers   prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
971643f4904SBrian Somers   prompt_Printf(arg->prompt, " State:              %s\n",
972643f4904SBrian Somers                 datalink_State(arg->cx));
973643f4904SBrian Somers   prompt_Printf(arg->prompt, " Peer name:          ");
974643f4904SBrian Somers   if (*arg->cx->peer.authname)
975643f4904SBrian Somers     prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
976643f4904SBrian Somers   else if (arg->cx->state == DATALINK_OPEN)
977643f4904SBrian Somers     prompt_Printf(arg->prompt, "None requested\n");
978565e35e5SBrian Somers   else
979643f4904SBrian Somers     prompt_Printf(arg->prompt, "N/A\n");
980643f4904SBrian Somers   prompt_Printf(arg->prompt, " Discriminator:      %s\n",
981643f4904SBrian Somers                 mp_Enddisc(arg->cx->peer.enddisc.class,
982643f4904SBrian Somers                            arg->cx->peer.enddisc.address,
983643f4904SBrian Somers                            arg->cx->peer.enddisc.len));
984643f4904SBrian Somers 
985643f4904SBrian Somers   prompt_Printf(arg->prompt, "\nDefaults:\n");
986643f4904SBrian Somers   prompt_Printf(arg->prompt, " Phone List:         %s\n",
987643f4904SBrian Somers                 arg->cx->cfg.phone.list);
988643f4904SBrian Somers   if (arg->cx->cfg.dial.max)
989643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         %d, delay ",
990643f4904SBrian Somers                   arg->cx->cfg.dial.max);
991565e35e5SBrian Somers   else
992643f4904SBrian Somers     prompt_Printf(arg->prompt, " Dial tries:         infinite, delay ");
993b5c347a3SBrian Somers   if (arg->cx->cfg.dial.next_timeout >= 0)
994643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
995565e35e5SBrian Somers   else
996643f4904SBrian Somers     prompt_Printf(arg->prompt, "random/");
997b5c347a3SBrian Somers   if (arg->cx->cfg.dial.timeout >= 0)
998643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
999565e35e5SBrian Somers   else
1000643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
1001643f4904SBrian Somers   prompt_Printf(arg->prompt, " Reconnect tries:    %d, delay ",
1002643f4904SBrian Somers                 arg->cx->cfg.reconnect.max);
1003643f4904SBrian Somers   if (arg->cx->cfg.reconnect.timeout > 0)
1004643f4904SBrian Somers     prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
1005643f4904SBrian Somers   else
1006643f4904SBrian Somers     prompt_Printf(arg->prompt, "random\n");
100792b09558SBrian Somers   prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
100892b09558SBrian Somers                 PHYS_DIRECT ?  "accepted: " : "requested:");
100992b09558SBrian Somers   if (!arg->cx->cfg.callback.opmask)
101092b09558SBrian Somers     prompt_Printf(arg->prompt, "none\n");
101192b09558SBrian Somers   else {
101292b09558SBrian Somers     int comma = 0;
101392b09558SBrian Somers 
101492b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
101592b09558SBrian Somers       prompt_Printf(arg->prompt, "none");
101692b09558SBrian Somers       comma = 1;
101792b09558SBrian Somers     }
101892b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
101992b09558SBrian Somers       prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
102092b09558SBrian Somers       comma = 1;
102192b09558SBrian Somers     }
102292b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
102392b09558SBrian Somers       prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
102492b09558SBrian Somers       if (arg->cx->physical->type != PHYS_DIRECT)
102592b09558SBrian Somers         prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
102692b09558SBrian Somers       comma = 1;
102792b09558SBrian Somers     }
102892b09558SBrian Somers     if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
102992b09558SBrian Somers       prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
103092b09558SBrian Somers       prompt_Printf(arg->prompt, " CBCP:               delay: %ds\n",
103192b09558SBrian Somers                     arg->cx->cfg.cbcp.delay);
1032cf784a89SBrian Somers       prompt_Printf(arg->prompt, "                     phone: ");
1033cf784a89SBrian Somers       if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
1034cf784a89SBrian Somers         if (arg->cx->physical->type & PHYS_DIRECT)
1035cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Caller decides\n");
1036cf784a89SBrian Somers         else
1037cf784a89SBrian Somers           prompt_Printf(arg->prompt, "Dialback server decides\n");
1038cf784a89SBrian Somers       } else
1039cf784a89SBrian Somers         prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
104092b09558SBrian Somers       prompt_Printf(arg->prompt, "                     timeout: %lds\n",
104192b09558SBrian Somers                     arg->cx->cfg.cbcp.fsmretry);
104292b09558SBrian Somers     } else
104392b09558SBrian Somers       prompt_Printf(arg->prompt, "\n");
104492b09558SBrian Somers   }
104592b09558SBrian Somers 
1046643f4904SBrian Somers   prompt_Printf(arg->prompt, " Dial Script:        %s\n",
1047643f4904SBrian Somers                 arg->cx->cfg.script.dial);
1048643f4904SBrian Somers   prompt_Printf(arg->prompt, " Login Script:       %s\n",
1049643f4904SBrian Somers                 arg->cx->cfg.script.login);
1050643f4904SBrian Somers   prompt_Printf(arg->prompt, " Hangup Script:      %s\n",
1051643f4904SBrian Somers                 arg->cx->cfg.script.hangup);
1052643f4904SBrian Somers   return 0;
1053565e35e5SBrian Somers }
1054565e35e5SBrian Somers 
1055565e35e5SBrian Somers int
1056565e35e5SBrian Somers datalink_SetReconnect(struct cmdargs const *arg)
1057565e35e5SBrian Somers {
105825092092SBrian Somers   if (arg->argc == arg->argn+2) {
105925092092SBrian Somers     arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
106025092092SBrian Somers     arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
1061565e35e5SBrian Somers     return 0;
1062565e35e5SBrian Somers   }
1063565e35e5SBrian Somers   return -1;
1064565e35e5SBrian Somers }
1065565e35e5SBrian Somers 
1066565e35e5SBrian Somers int
1067565e35e5SBrian Somers datalink_SetRedial(struct cmdargs const *arg)
1068565e35e5SBrian Somers {
1069c11e57a3SBrian Somers   const char *sep, *osep;
1070c11e57a3SBrian Somers   int timeout, inc, maxinc, tries;
1071565e35e5SBrian Somers 
107225092092SBrian Somers   if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
107325092092SBrian Somers     if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
107425092092SBrian Somers 	(arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
1075565e35e5SBrian Somers       arg->cx->cfg.dial.timeout = -1;
1076565e35e5SBrian Somers       randinit();
1077565e35e5SBrian Somers     } else {
107825092092SBrian Somers       timeout = atoi(arg->argv[arg->argn]);
1079565e35e5SBrian Somers 
1080565e35e5SBrian Somers       if (timeout >= 0)
1081565e35e5SBrian Somers 	arg->cx->cfg.dial.timeout = timeout;
1082565e35e5SBrian Somers       else {
1083dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid redial timeout\n");
1084565e35e5SBrian Somers 	return -1;
1085565e35e5SBrian Somers       }
1086565e35e5SBrian Somers     }
1087565e35e5SBrian Somers 
1088c11e57a3SBrian Somers     sep = strchr(arg->argv[arg->argn], '+');
1089c11e57a3SBrian Somers     if (sep) {
1090c11e57a3SBrian Somers       inc = atoi(++sep);
1091c11e57a3SBrian Somers       osep = sep;
1092c11e57a3SBrian Somers       if (inc >= 0)
1093c11e57a3SBrian Somers         arg->cx->cfg.dial.inc = inc;
1094c11e57a3SBrian Somers       else {
1095c11e57a3SBrian Somers         log_Printf(LogWARN, "Invalid timeout increment\n");
1096c11e57a3SBrian Somers         return -1;
1097c11e57a3SBrian Somers       }
1098c11e57a3SBrian Somers       sep = strchr(sep, '-');
1099c11e57a3SBrian Somers       if (sep) {
1100c11e57a3SBrian Somers         maxinc = atoi(++sep);
1101c11e57a3SBrian Somers         if (maxinc >= 0)
1102c11e57a3SBrian Somers           arg->cx->cfg.dial.maxinc = maxinc;
1103c11e57a3SBrian Somers         else {
1104c11e57a3SBrian Somers           log_Printf(LogWARN, "Invalid maximum timeout increments\n");
1105c11e57a3SBrian Somers           return -1;
1106c11e57a3SBrian Somers         }
1107c11e57a3SBrian Somers       } else {
1108c11e57a3SBrian Somers         /* Default timeout increment */
1109c11e57a3SBrian Somers         arg->cx->cfg.dial.maxinc = 10;
1110c11e57a3SBrian Somers         sep = osep;
1111c11e57a3SBrian Somers       }
1112c11e57a3SBrian Somers     } else {
1113c11e57a3SBrian Somers       /* Default timeout increment & max increment */
1114c11e57a3SBrian Somers       arg->cx->cfg.dial.inc = 0;
1115c11e57a3SBrian Somers       arg->cx->cfg.dial.maxinc = 10;
1116c11e57a3SBrian Somers       sep = arg->argv[arg->argn];
1117c11e57a3SBrian Somers     }
1118c11e57a3SBrian Somers 
1119c11e57a3SBrian Somers     sep = strchr(sep, '.');
1120c11e57a3SBrian Somers     if (sep) {
1121c11e57a3SBrian Somers       if (strcasecmp(++sep, "random") == 0) {
1122565e35e5SBrian Somers 	arg->cx->cfg.dial.next_timeout = -1;
1123565e35e5SBrian Somers 	randinit();
1124565e35e5SBrian Somers       } else {
1125c11e57a3SBrian Somers 	timeout = atoi(sep);
1126565e35e5SBrian Somers 	if (timeout >= 0)
1127565e35e5SBrian Somers 	  arg->cx->cfg.dial.next_timeout = timeout;
1128565e35e5SBrian Somers 	else {
1129dd7e2610SBrian Somers 	  log_Printf(LogWARN, "Invalid next redial timeout\n");
1130565e35e5SBrian Somers 	  return -1;
1131565e35e5SBrian Somers 	}
1132565e35e5SBrian Somers       }
1133565e35e5SBrian Somers     } else
1134565e35e5SBrian Somers       /* Default next timeout */
1135565e35e5SBrian Somers       arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
1136565e35e5SBrian Somers 
113725092092SBrian Somers     if (arg->argc == arg->argn+2) {
113825092092SBrian Somers       tries = atoi(arg->argv[arg->argn+1]);
1139565e35e5SBrian Somers 
1140565e35e5SBrian Somers       if (tries >= 0) {
1141565e35e5SBrian Somers 	arg->cx->cfg.dial.max = tries;
1142565e35e5SBrian Somers       } else {
1143dd7e2610SBrian Somers 	log_Printf(LogWARN, "Invalid retry value\n");
1144565e35e5SBrian Somers 	return 1;
1145565e35e5SBrian Somers       }
1146565e35e5SBrian Somers     }
1147565e35e5SBrian Somers     return 0;
1148565e35e5SBrian Somers   }
1149c11e57a3SBrian Somers 
1150565e35e5SBrian Somers   return -1;
1151c7cc5030SBrian Somers }
1152c7cc5030SBrian Somers 
1153cdbbb6b5SBrian Somers static const char *states[] = {
1154565e35e5SBrian Somers   "closed",
1155565e35e5SBrian Somers   "opening",
1156565e35e5SBrian Somers   "hangup",
1157565e35e5SBrian Somers   "dial",
1158565e35e5SBrian Somers   "login",
1159565e35e5SBrian Somers   "ready",
1160565e35e5SBrian Somers   "lcp",
1161565e35e5SBrian Somers   "auth",
116292b09558SBrian Somers   "cbcp",
1163565e35e5SBrian Somers   "open"
1164c7cc5030SBrian Somers };
1165c7cc5030SBrian Somers 
1166643f4904SBrian Somers const char *
1167c7cc5030SBrian Somers datalink_State(struct datalink *dl)
1168c7cc5030SBrian Somers {
1169c7cc5030SBrian Somers   if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0])
1170c7cc5030SBrian Somers     return "unknown";
1171c7cc5030SBrian Somers   return states[dl->state];
1172c7cc5030SBrian Somers }
11736f384573SBrian Somers 
11749ae58882SBrian Somers static void
11759ae58882SBrian Somers datalink_NewState(struct datalink *dl, int state)
11769ae58882SBrian Somers {
11779ae58882SBrian Somers   if (state != dl->state) {
11789ae58882SBrian Somers     if (state >= 0 && state < sizeof states / sizeof states[0]) {
11799ae58882SBrian Somers       log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
11809ae58882SBrian Somers                  states[state]);
11819ae58882SBrian Somers       dl->state = state;
11829ae58882SBrian Somers     } else
11839ae58882SBrian Somers       log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
11849ae58882SBrian Somers   }
11859ae58882SBrian Somers }
11869ae58882SBrian Somers 
11876f384573SBrian Somers struct datalink *
118896c9bb21SBrian Somers iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
118996c9bb21SBrian Somers              int fd)
11906f384573SBrian Somers {
1191b7c5748eSBrian Somers   struct datalink *dl, *cdl;
1192479508cfSBrian Somers   struct fsm_retry copy;
1193b7c5748eSBrian Somers   char *oname;
11946f384573SBrian Somers 
119596c9bb21SBrian Somers   dl = (struct datalink *)iov[(*niov)++].iov_base;
119696c9bb21SBrian Somers   dl->name = iov[*niov].iov_base;
11976f384573SBrian Somers 
119896c9bb21SBrian Somers   if (dl->name[DATALINK_MAXNAME-1]) {
119996c9bb21SBrian Somers     dl->name[DATALINK_MAXNAME-1] = '\0';
120096c9bb21SBrian Somers     if (strlen(dl->name) == DATALINK_MAXNAME - 1)
120196c9bb21SBrian Somers       log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
12026f384573SBrian Somers   }
1203b7c5748eSBrian Somers 
1204b7c5748eSBrian Somers   /* Make sure the name is unique ! */
1205b7c5748eSBrian Somers   oname = NULL;
1206b7c5748eSBrian Somers   do {
1207b7c5748eSBrian Somers     for (cdl = bundle->links; cdl; cdl = cdl->next)
1208b7c5748eSBrian Somers       if (!strcasecmp(dl->name, cdl->name)) {
1209b7c5748eSBrian Somers         if (oname)
1210b7c5748eSBrian Somers           free(datalink_NextName(dl));
1211b7c5748eSBrian Somers         else
1212b7c5748eSBrian Somers           oname = datalink_NextName(dl);
1213b7c5748eSBrian Somers         break;	/* Keep renaming 'till we have no conflicts */
1214b7c5748eSBrian Somers       }
1215b7c5748eSBrian Somers   } while (cdl);
1216b7c5748eSBrian Somers 
1217b7c5748eSBrian Somers   if (oname) {
1218b7c5748eSBrian Somers     log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
1219b7c5748eSBrian Somers     free(oname);
1220b7c5748eSBrian Somers   } else {
122196c9bb21SBrian Somers     dl->name = strdup(dl->name);
1222b7c5748eSBrian Somers     free(iov[*niov].iov_base);
1223b7c5748eSBrian Somers   }
1224b7c5748eSBrian Somers   (*niov)++;
12256f384573SBrian Somers 
12266f384573SBrian Somers   dl->desc.type = DATALINK_DESCRIPTOR;
12276f384573SBrian Somers   dl->desc.UpdateSet = datalink_UpdateSet;
12286f384573SBrian Somers   dl->desc.IsSet = datalink_IsSet;
12296f384573SBrian Somers   dl->desc.Read = datalink_Read;
12306f384573SBrian Somers   dl->desc.Write = datalink_Write;
12316f384573SBrian Somers 
12326f384573SBrian Somers   mp_linkInit(&dl->mp);
12336f384573SBrian Somers   *dl->phone.list = '\0';
12346f384573SBrian Somers   dl->phone.next = NULL;
12356f384573SBrian Somers   dl->phone.alt = NULL;
12366f384573SBrian Somers   dl->phone.chosen = "N/A";
12376f384573SBrian Somers 
12386f384573SBrian Somers   dl->bundle = bundle;
12396f384573SBrian Somers   dl->next = NULL;
1240c11e57a3SBrian Somers   memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
1241c11e57a3SBrian Somers   dl->dial.tries = 0;
12426f384573SBrian Somers   dl->reconnect_tries = 0;
12436f384573SBrian Somers   dl->parent = &bundle->fsm;
12446f384573SBrian Somers   dl->fsmp.LayerStart = datalink_LayerStart;
12456f384573SBrian Somers   dl->fsmp.LayerUp = datalink_LayerUp;
12466f384573SBrian Somers   dl->fsmp.LayerDown = datalink_LayerDown;
12476f384573SBrian Somers   dl->fsmp.LayerFinish = datalink_LayerFinish;
12486f384573SBrian Somers   dl->fsmp.object = dl;
12496f384573SBrian Somers 
125096c9bb21SBrian Somers   dl->physical = iov2modem(dl, iov, niov, maxiov, fd);
125196c9bb21SBrian Somers 
125296c9bb21SBrian Somers   if (!dl->physical) {
12536f384573SBrian Somers     free(dl->name);
12546f384573SBrian Somers     free(dl);
12556f384573SBrian Somers     dl = NULL;
12569ae58882SBrian Somers   } else {
1257479508cfSBrian Somers     copy = dl->pap.cfg.fsm;
1258f0cdd9c0SBrian Somers     pap_Init(&dl->pap, dl->physical);
1259479508cfSBrian Somers     dl->pap.cfg.fsm = copy;
1260f0cdd9c0SBrian Somers 
1261479508cfSBrian Somers     copy = dl->chap.auth.cfg.fsm;
1262f0cdd9c0SBrian Somers     chap_Init(&dl->chap, dl->physical);
1263479508cfSBrian Somers     dl->chap.auth.cfg.fsm = copy;
1264f0cdd9c0SBrian Somers 
126592b09558SBrian Somers     cbcp_Init(&dl->cbcp, dl->physical);
12666f384573SBrian Somers     chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
12676f384573SBrian Somers 
12689ae58882SBrian Somers     log_Printf(LogPHASE, "%s: Transferred in %s state\n",
12699ae58882SBrian Somers               dl->name, datalink_State(dl));
12709ae58882SBrian Somers   }
12719ae58882SBrian Somers 
12726f384573SBrian Somers   return dl;
12736f384573SBrian Somers }
12746f384573SBrian Somers 
12756f384573SBrian Somers int
127685fd273aSBrian Somers datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
127785fd273aSBrian Somers              pid_t newpid)
12786f384573SBrian Somers {
127996c9bb21SBrian Somers   /* If `dl' is NULL, we're allocating before a Fromiov() */
128096c9bb21SBrian Somers   int link_fd;
12816f384573SBrian Somers 
128296c9bb21SBrian Somers   if (dl) {
1283c11e57a3SBrian Somers     timer_Stop(&dl->dial.timer);
128492b09558SBrian Somers     /* The following is purely for the sake of paranoia */
128592b09558SBrian Somers     cbcp_Down(&dl->cbcp);
1286dd7e2610SBrian Somers     timer_Stop(&dl->pap.authtimer);
1287dd7e2610SBrian Somers     timer_Stop(&dl->chap.auth.authtimer);
12886f384573SBrian Somers   }
12896f384573SBrian Somers 
129096c9bb21SBrian Somers   if (*niov >= maxiov - 1) {
129196c9bb21SBrian Somers     log_Printf(LogERROR, "Toiov: No room for datalink !\n");
129296c9bb21SBrian Somers     if (dl) {
12936f384573SBrian Somers       free(dl->name);
12946f384573SBrian Somers       free(dl);
129596c9bb21SBrian Somers     }
129696c9bb21SBrian Somers     return -1;
129796c9bb21SBrian Somers   }
129896c9bb21SBrian Somers 
129996c9bb21SBrian Somers   iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl);
130096c9bb21SBrian Somers   iov[(*niov)++].iov_len = sizeof *dl;
130196c9bb21SBrian Somers   iov[*niov].iov_base =
130296c9bb21SBrian Somers     dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
130396c9bb21SBrian Somers   iov[(*niov)++].iov_len = DATALINK_MAXNAME;
130496c9bb21SBrian Somers 
130585fd273aSBrian Somers   link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
130696c9bb21SBrian Somers 
130796c9bb21SBrian Somers   if (link_fd == -1 && dl) {
130896c9bb21SBrian Somers     free(dl->name);
130996c9bb21SBrian Somers     free(dl);
131096c9bb21SBrian Somers   }
13116f384573SBrian Somers 
13126f384573SBrian Somers   return link_fd;
13136f384573SBrian Somers }
13146f384573SBrian Somers 
131558d55334SBrian Somers void
131658d55334SBrian Somers datalink_Rename(struct datalink *dl, const char *name)
131758d55334SBrian Somers {
131858d55334SBrian Somers   free(dl->name);
131958d55334SBrian Somers   dl->physical->link.name = dl->name = strdup(name);
132058d55334SBrian Somers }
132158d55334SBrian Somers 
132284917b87SBrian Somers char *
132384917b87SBrian Somers datalink_NextName(struct datalink *dl)
13246f384573SBrian Somers {
13256f384573SBrian Somers   int f, n;
132684917b87SBrian Somers   char *name, *oname;
13276f384573SBrian Somers 
13286f384573SBrian Somers   n = strlen(dl->name);
13296f384573SBrian Somers   name = (char *)malloc(n+3);
13306f384573SBrian Somers   for (f = n - 1; f >= 0; f--)
13316f384573SBrian Somers     if (!isdigit(dl->name[f]))
13326f384573SBrian Somers       break;
13336f384573SBrian Somers   n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
13346f384573SBrian Somers   sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
133584917b87SBrian Somers   oname = dl->name;
133654cd8e13SBrian Somers   dl->name = name;
133754cd8e13SBrian Somers   /* our physical link name isn't updated (it probably isn't created yet) */
133884917b87SBrian Somers   return oname;
13396f384573SBrian Somers }
1340dd0645c5SBrian Somers 
1341dd0645c5SBrian Somers int
1342dd0645c5SBrian Somers datalink_SetMode(struct datalink *dl, int mode)
1343dd0645c5SBrian Somers {
1344dd0645c5SBrian Somers   if (!physical_SetMode(dl->physical, mode))
1345dd0645c5SBrian Somers     return 0;
1346dd0645c5SBrian Somers   if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
1347dd0645c5SBrian Somers     dl->script.run = 0;
1348dd0645c5SBrian Somers   if (dl->physical->type == PHYS_DIRECT)
1349dd0645c5SBrian Somers     dl->reconnect_tries = 0;
135081358fa3SBrian Somers   if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY)
1351dd0645c5SBrian Somers     datalink_Up(dl, 1, 1);
1352dd0645c5SBrian Somers   return 1;
1353dd0645c5SBrian Somers }
1354c11e57a3SBrian Somers 
1355c11e57a3SBrian Somers int
1356c11e57a3SBrian Somers datalink_GetDialTimeout(struct datalink *dl)
1357c11e57a3SBrian Somers {
1358c11e57a3SBrian Somers   int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
1359c11e57a3SBrian Somers 
1360c11e57a3SBrian Somers   if (dl->dial.incs < dl->cfg.dial.maxinc)
1361c11e57a3SBrian Somers     dl->dial.incs++;
1362c11e57a3SBrian Somers 
1363c11e57a3SBrian Somers   return result;
1364c11e57a3SBrian Somers }
1365