datalink.c revision 45350
136285Sbrian/*- 236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 336285Sbrian * All rights reserved. 436285Sbrian * 536285Sbrian * Redistribution and use in source and binary forms, with or without 636285Sbrian * modification, are permitted provided that the following conditions 736285Sbrian * are met: 836285Sbrian * 1. Redistributions of source code must retain the above copyright 936285Sbrian * notice, this list of conditions and the following disclaimer. 1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1136285Sbrian * notice, this list of conditions and the following disclaimer in the 1236285Sbrian * documentation and/or other materials provided with the distribution. 1336285Sbrian * 1436285Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1536285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1736285Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1836285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1936285Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2036285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2236285Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2336285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2436285Sbrian * SUCH DAMAGE. 2536285Sbrian * 2645350Sbrian * $Id: datalink.c,v 1.35 1999/03/04 17:42:15 brian Exp $ 2736285Sbrian */ 2836285Sbrian 2943313Sbrian#include <sys/param.h> 3036285Sbrian#include <netinet/in.h> 3136285Sbrian#include <netinet/in_systm.h> 3236285Sbrian#include <netinet/ip.h> 3336285Sbrian#include <sys/un.h> 3436285Sbrian 3536285Sbrian#include <ctype.h> 3636285Sbrian#include <stdio.h> 3736285Sbrian#include <stdlib.h> 3836285Sbrian#include <string.h> 3936285Sbrian#include <sys/uio.h> 4036285Sbrian#include <termios.h> 4136285Sbrian 4236285Sbrian#include "mbuf.h" 4336285Sbrian#include "log.h" 4436285Sbrian#include "defs.h" 4536285Sbrian#include "timer.h" 4636285Sbrian#include "fsm.h" 4736285Sbrian#include "lcp.h" 4836285Sbrian#include "descriptor.h" 4936285Sbrian#include "lqr.h" 5036285Sbrian#include "hdlc.h" 5136285Sbrian#include "async.h" 5236285Sbrian#include "throughput.h" 5336285Sbrian#include "ccp.h" 5436285Sbrian#include "link.h" 5536285Sbrian#include "physical.h" 5636285Sbrian#include "iplist.h" 5736285Sbrian#include "slcompress.h" 5836285Sbrian#include "ipcp.h" 5936285Sbrian#include "filter.h" 6036285Sbrian#include "mp.h" 6143313Sbrian#ifndef NORADIUS 6243313Sbrian#include "radius.h" 6343313Sbrian#endif 6436285Sbrian#include "bundle.h" 6536285Sbrian#include "chat.h" 6636285Sbrian#include "auth.h" 6736285Sbrian#include "modem.h" 6836285Sbrian#include "prompt.h" 6936285Sbrian#include "lcpproto.h" 7036285Sbrian#include "pap.h" 7136285Sbrian#include "chap.h" 7236285Sbrian#include "command.h" 7338174Sbrian#include "cbcp.h" 7436285Sbrian#include "datalink.h" 7536285Sbrian 7636285Sbrianstatic void datalink_LoginDone(struct datalink *); 7736285Sbrianstatic void datalink_NewState(struct datalink *, int); 7836285Sbrian 7936285Sbrianstatic void 8036285Sbriandatalink_OpenTimeout(void *v) 8136285Sbrian{ 8236285Sbrian struct datalink *dl = (struct datalink *)v; 8336285Sbrian 8444468Sbrian timer_Stop(&dl->dial.timer); 8536285Sbrian if (dl->state == DATALINK_OPENING) 8636285Sbrian log_Printf(LogPHASE, "%s: Redial timer expired.\n", dl->name); 8736285Sbrian} 8836285Sbrian 8944261Sbrianstatic int 9036285Sbriandatalink_StartDialTimer(struct datalink *dl, int Timeout) 9136285Sbrian{ 9244261Sbrian int result = Timeout; 9344261Sbrian 9444468Sbrian timer_Stop(&dl->dial.timer); 9538174Sbrian if (Timeout) { 9636285Sbrian if (Timeout > 0) 9744468Sbrian dl->dial.timer.load = Timeout * SECTICKS; 9844261Sbrian else { 9944261Sbrian result = (random() % DIAL_TIMEOUT) + 1; 10044468Sbrian dl->dial.timer.load = result * SECTICKS; 10144261Sbrian } 10244468Sbrian dl->dial.timer.func = datalink_OpenTimeout; 10344468Sbrian dl->dial.timer.name = "dial"; 10444468Sbrian dl->dial.timer.arg = dl; 10544468Sbrian timer_Start(&dl->dial.timer); 10636285Sbrian if (dl->state == DATALINK_OPENING) 10736285Sbrian log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n", 10836285Sbrian dl->name, Timeout); 10936285Sbrian } 11044261Sbrian return result; 11136285Sbrian} 11236285Sbrian 11336285Sbrianstatic void 11436285Sbriandatalink_HangupDone(struct datalink *dl) 11536285Sbrian{ 11636285Sbrian if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp && 11736285Sbrian physical_GetFD(dl->physical) != -1) { 11836285Sbrian /* Don't close our modem if the link is dedicated */ 11936285Sbrian datalink_LoginDone(dl); 12036285Sbrian return; 12136285Sbrian } 12236285Sbrian 12336285Sbrian modem_Close(dl->physical); 12436285Sbrian dl->phone.chosen = "N/A"; 12536285Sbrian 12638174Sbrian if (dl->cbcp.required) { 12738174Sbrian log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone); 12838174Sbrian dl->cfg.callback.opmask = 0; 12938174Sbrian strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone, 13038174Sbrian sizeof dl->cfg.phone.list - 1); 13138174Sbrian dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0'; 13238174Sbrian dl->phone.alt = dl->phone.next = NULL; 13338174Sbrian dl->reconnect_tries = dl->cfg.reconnect.max; 13444468Sbrian dl->dial.tries = dl->cfg.dial.max; 13544468Sbrian dl->dial.incs = 0; 13638174Sbrian dl->script.run = 1; 13738174Sbrian dl->script.packetmode = 1; 13838174Sbrian if (!physical_SetMode(dl->physical, PHYS_BACKGROUND)) 13938174Sbrian log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n"); 14038174Sbrian bundle_LinksRemoved(dl->bundle); 14144468Sbrian /* if dial.timeout is < 0 (random), we don't override fsm.delay */ 14238174Sbrian if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout) 14338174Sbrian dl->cbcp.fsm.delay = dl->cfg.dial.timeout; 14438174Sbrian datalink_StartDialTimer(dl, dl->cbcp.fsm.delay); 14538174Sbrian cbcp_Down(&dl->cbcp); 14638174Sbrian datalink_NewState(dl, DATALINK_OPENING); 14738174Sbrian } else if (dl->bundle->CleaningUp || 14836285Sbrian (dl->physical->type == PHYS_DIRECT) || 14944468Sbrian ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) && 15036465Sbrian !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) { 15136285Sbrian datalink_NewState(dl, DATALINK_CLOSED); 15244468Sbrian dl->dial.tries = -1; 15344468Sbrian dl->dial.incs = 0; 15436285Sbrian dl->reconnect_tries = 0; 15536285Sbrian bundle_LinkClosed(dl->bundle, dl); 15636285Sbrian if (!dl->bundle->CleaningUp) 15744468Sbrian datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl)); 15836285Sbrian } else { 15936285Sbrian datalink_NewState(dl, DATALINK_OPENING); 16044468Sbrian if (dl->dial.tries < 0) { 16136285Sbrian datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout); 16244468Sbrian dl->dial.tries = dl->cfg.dial.max; 16344468Sbrian dl->dial.incs = 0; 16436285Sbrian dl->reconnect_tries--; 16536285Sbrian } else { 16636285Sbrian if (dl->phone.next == NULL) 16744468Sbrian datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl)); 16836285Sbrian else 16936285Sbrian datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout); 17036285Sbrian } 17136285Sbrian } 17236285Sbrian} 17336285Sbrian 17436285Sbrianstatic const char * 17536285Sbriandatalink_ChoosePhoneNumber(struct datalink *dl) 17636285Sbrian{ 17736285Sbrian char *phone; 17836285Sbrian 17936285Sbrian if (dl->phone.alt == NULL) { 18036285Sbrian if (dl->phone.next == NULL) { 18136285Sbrian strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1); 18236285Sbrian dl->phone.list[sizeof dl->phone.list - 1] = '\0'; 18336285Sbrian dl->phone.next = dl->phone.list; 18436285Sbrian } 18536285Sbrian dl->phone.alt = strsep(&dl->phone.next, ":"); 18636285Sbrian } 18736285Sbrian phone = strsep(&dl->phone.alt, "|"); 18836285Sbrian dl->phone.chosen = *phone ? phone : "[NONE]"; 18936285Sbrian if (*phone) 19036285Sbrian log_Printf(LogPHASE, "Phone: %s\n", phone); 19136285Sbrian return phone; 19236285Sbrian} 19336285Sbrian 19436285Sbrianstatic void 19536285Sbriandatalink_LoginDone(struct datalink *dl) 19636285Sbrian{ 19736285Sbrian if (!dl->script.packetmode) { 19844468Sbrian dl->dial.tries = -1; 19944468Sbrian dl->dial.incs = 0; 20036285Sbrian datalink_NewState(dl, DATALINK_READY); 20136285Sbrian } else if (modem_Raw(dl->physical, dl->bundle) < 0) { 20244468Sbrian dl->dial.tries = 0; 20336285Sbrian log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n"); 20436285Sbrian if (dl->script.run) { 20536285Sbrian datalink_NewState(dl, DATALINK_HANGUP); 20636285Sbrian modem_Offline(dl->physical); 20736285Sbrian chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); 20836285Sbrian } else { 20937360Sbrian timer_Stop(&dl->physical->Timer); 21036285Sbrian if (dl->physical->type == PHYS_DEDICATED) 21136285Sbrian /* force a redial timeout */ 21236285Sbrian modem_Close(dl->physical); 21336285Sbrian datalink_HangupDone(dl); 21436285Sbrian } 21536285Sbrian } else { 21644468Sbrian dl->dial.tries = -1; 21744468Sbrian dl->dial.incs = 0; 21836285Sbrian 21936285Sbrian hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp); 22036285Sbrian async_Init(&dl->physical->async); 22136285Sbrian 22236285Sbrian lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ? 22336285Sbrian 0 : dl->physical->link.lcp.cfg.openmode); 22436285Sbrian ccp_Setup(&dl->physical->link.ccp); 22536285Sbrian 22636285Sbrian datalink_NewState(dl, DATALINK_LCP); 22736285Sbrian fsm_Up(&dl->physical->link.lcp.fsm); 22836285Sbrian fsm_Open(&dl->physical->link.lcp.fsm); 22936285Sbrian } 23036285Sbrian} 23136285Sbrian 23236285Sbrianstatic int 23336285Sbriandatalink_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, 23436285Sbrian int *n) 23536285Sbrian{ 23636285Sbrian struct datalink *dl = descriptor2datalink(d); 23736285Sbrian int result; 23836285Sbrian 23936285Sbrian result = 0; 24036285Sbrian switch (dl->state) { 24136285Sbrian case DATALINK_CLOSED: 24236465Sbrian if ((dl->physical->type & 24336465Sbrian (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|PHYS_DDIAL)) && 24436285Sbrian !bundle_IsDead(dl->bundle)) 24536285Sbrian /* 24636465Sbrian * Our first time in - DEDICATED & DDIAL never come down, and 24736465Sbrian * DIRECT & BACKGROUND get deleted when they enter DATALINK_CLOSED. 24836465Sbrian * Go to DATALINK_OPENING via datalink_Up() and fall through. 24936285Sbrian */ 25036285Sbrian datalink_Up(dl, 1, 1); 25136285Sbrian else 25236285Sbrian break; 25336285Sbrian /* fall through */ 25436285Sbrian 25536285Sbrian case DATALINK_OPENING: 25644468Sbrian if (dl->dial.timer.state != TIMER_RUNNING) { 25744468Sbrian if (--dl->dial.tries < 0) 25844468Sbrian dl->dial.tries = 0; 25936285Sbrian if (modem_Open(dl->physical, dl->bundle) >= 0) { 26038200Sbrian log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n" 26138200Sbrian "Type `~?' for help\r\n", dl->name, 26238200Sbrian dl->physical->name.full); 26336285Sbrian if (dl->script.run) { 26436285Sbrian datalink_NewState(dl, DATALINK_DIAL); 26536285Sbrian chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1, 26636285Sbrian datalink_ChoosePhoneNumber(dl)); 26736465Sbrian if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) && 26836285Sbrian dl->cfg.dial.max) 26936285Sbrian log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n", 27044468Sbrian dl->name, dl->cfg.dial.max - dl->dial.tries, 27136285Sbrian dl->cfg.dial.max); 27236285Sbrian } else 27336285Sbrian datalink_LoginDone(dl); 27441830Sbrian return datalink_UpdateSet(d, r, w, e, n); 27536285Sbrian } else { 27636465Sbrian if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) && 27736285Sbrian dl->cfg.dial.max) 27836285Sbrian log_Printf(LogCHAT, "Failed to open modem (attempt %u of %d)\n", 27944468Sbrian dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max); 28036285Sbrian else 28136285Sbrian log_Printf(LogCHAT, "Failed to open modem\n"); 28236285Sbrian 28336285Sbrian if (dl->bundle->CleaningUp || 28436465Sbrian (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) && 28544468Sbrian dl->cfg.dial.max && dl->dial.tries == 0)) { 28636285Sbrian datalink_NewState(dl, DATALINK_CLOSED); 28736285Sbrian dl->reconnect_tries = 0; 28844468Sbrian dl->dial.tries = -1; 28938200Sbrian log_WritePrompts(dl, "Failed to open %s\n", 29038200Sbrian dl->physical->name.full); 29136285Sbrian bundle_LinkClosed(dl->bundle, dl); 29236285Sbrian } 29338200Sbrian if (!dl->bundle->CleaningUp) { 29444468Sbrian int timeout; 29544468Sbrian 29644468Sbrian timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl)); 29738200Sbrian log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n", 29844261Sbrian dl->physical->name.full, timeout); 29938200Sbrian } 30036285Sbrian } 30136285Sbrian } 30236285Sbrian break; 30336285Sbrian 30436285Sbrian case DATALINK_HANGUP: 30536285Sbrian case DATALINK_DIAL: 30636285Sbrian case DATALINK_LOGIN: 30736285Sbrian result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n); 30836285Sbrian switch (dl->chat.state) { 30936285Sbrian case CHAT_DONE: 31036285Sbrian /* script succeeded */ 31136285Sbrian chat_Destroy(&dl->chat); 31236285Sbrian switch(dl->state) { 31336285Sbrian case DATALINK_HANGUP: 31436285Sbrian datalink_HangupDone(dl); 31536285Sbrian break; 31636285Sbrian case DATALINK_DIAL: 31736285Sbrian datalink_NewState(dl, DATALINK_LOGIN); 31836285Sbrian chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL); 31936285Sbrian return datalink_UpdateSet(d, r, w, e, n); 32036285Sbrian case DATALINK_LOGIN: 32142390Sbrian dl->phone.alt = NULL; 32236285Sbrian datalink_LoginDone(dl); 32342905Sbrian return datalink_UpdateSet(d, r, w, e, n); 32436285Sbrian } 32536285Sbrian break; 32636285Sbrian case CHAT_FAILED: 32736285Sbrian /* Going down - script failed */ 32836285Sbrian log_Printf(LogWARN, "Chat script failed\n"); 32936285Sbrian chat_Destroy(&dl->chat); 33036285Sbrian switch(dl->state) { 33136285Sbrian case DATALINK_HANGUP: 33236285Sbrian datalink_HangupDone(dl); 33336285Sbrian break; 33436285Sbrian case DATALINK_DIAL: 33536285Sbrian case DATALINK_LOGIN: 33636285Sbrian datalink_NewState(dl, DATALINK_HANGUP); 33737360Sbrian modem_Offline(dl->physical); /* Is this required ? */ 33836285Sbrian chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); 33936285Sbrian return datalink_UpdateSet(d, r, w, e, n); 34036285Sbrian } 34136285Sbrian break; 34236285Sbrian } 34336285Sbrian break; 34436285Sbrian 34536285Sbrian case DATALINK_READY: 34636285Sbrian case DATALINK_LCP: 34736285Sbrian case DATALINK_AUTH: 34838174Sbrian case DATALINK_CBCP: 34936285Sbrian case DATALINK_OPEN: 35043888Sbrian result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) + 35143888Sbrian descriptor_UpdateSet(&dl->physical->desc, r, w, e, n); 35236285Sbrian break; 35336285Sbrian } 35436285Sbrian return result; 35536285Sbrian} 35636285Sbrian 35736285Sbrianint 35836285Sbriandatalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e) 35936285Sbrian{ 36036285Sbrian return physical_RemoveFromSet(dl->physical, r, w, e); 36136285Sbrian} 36236285Sbrian 36336285Sbrianstatic int 36436285Sbriandatalink_IsSet(struct descriptor *d, const fd_set *fdset) 36536285Sbrian{ 36636285Sbrian struct datalink *dl = descriptor2datalink(d); 36736285Sbrian 36836285Sbrian switch (dl->state) { 36936285Sbrian case DATALINK_CLOSED: 37036285Sbrian case DATALINK_OPENING: 37136285Sbrian break; 37236285Sbrian 37336285Sbrian case DATALINK_HANGUP: 37436285Sbrian case DATALINK_DIAL: 37536285Sbrian case DATALINK_LOGIN: 37636285Sbrian return descriptor_IsSet(&dl->chat.desc, fdset); 37736285Sbrian 37836285Sbrian case DATALINK_READY: 37936285Sbrian case DATALINK_LCP: 38036285Sbrian case DATALINK_AUTH: 38138174Sbrian case DATALINK_CBCP: 38236285Sbrian case DATALINK_OPEN: 38343888Sbrian return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 : 38443888Sbrian descriptor_IsSet(&dl->physical->desc, fdset); 38536285Sbrian } 38636285Sbrian return 0; 38736285Sbrian} 38836285Sbrian 38936285Sbrianstatic void 39036285Sbriandatalink_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 39136285Sbrian{ 39236285Sbrian struct datalink *dl = descriptor2datalink(d); 39336285Sbrian 39436285Sbrian switch (dl->state) { 39536285Sbrian case DATALINK_CLOSED: 39636285Sbrian case DATALINK_OPENING: 39736285Sbrian break; 39836285Sbrian 39936285Sbrian case DATALINK_HANGUP: 40036285Sbrian case DATALINK_DIAL: 40136285Sbrian case DATALINK_LOGIN: 40236285Sbrian descriptor_Read(&dl->chat.desc, bundle, fdset); 40336285Sbrian break; 40436285Sbrian 40536285Sbrian case DATALINK_READY: 40636285Sbrian case DATALINK_LCP: 40736285Sbrian case DATALINK_AUTH: 40838174Sbrian case DATALINK_CBCP: 40936285Sbrian case DATALINK_OPEN: 41043888Sbrian if (descriptor_IsSet(&dl->chap.desc, fdset)) 41143888Sbrian descriptor_Read(&dl->chap.desc, bundle, fdset); 41243888Sbrian if (descriptor_IsSet(&dl->physical->desc, fdset)) 41343888Sbrian descriptor_Read(&dl->physical->desc, bundle, fdset); 41436285Sbrian break; 41536285Sbrian } 41636285Sbrian} 41736285Sbrian 41837141Sbrianstatic int 41936285Sbriandatalink_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 42036285Sbrian{ 42136285Sbrian struct datalink *dl = descriptor2datalink(d); 42237141Sbrian int result = 0; 42336285Sbrian 42436285Sbrian switch (dl->state) { 42536285Sbrian case DATALINK_CLOSED: 42636285Sbrian case DATALINK_OPENING: 42736285Sbrian break; 42836285Sbrian 42936285Sbrian case DATALINK_HANGUP: 43036285Sbrian case DATALINK_DIAL: 43136285Sbrian case DATALINK_LOGIN: 43237141Sbrian result = descriptor_Write(&dl->chat.desc, bundle, fdset); 43336285Sbrian break; 43436285Sbrian 43536285Sbrian case DATALINK_READY: 43636285Sbrian case DATALINK_LCP: 43736285Sbrian case DATALINK_AUTH: 43838174Sbrian case DATALINK_CBCP: 43936285Sbrian case DATALINK_OPEN: 44043888Sbrian if (descriptor_IsSet(&dl->chap.desc, fdset)) 44143888Sbrian result += descriptor_Write(&dl->chap.desc, bundle, fdset); 44243888Sbrian if (descriptor_IsSet(&dl->physical->desc, fdset)) 44343888Sbrian result += descriptor_Write(&dl->physical->desc, bundle, fdset); 44436285Sbrian break; 44536285Sbrian } 44637141Sbrian 44737141Sbrian return result; 44836285Sbrian} 44936285Sbrian 45036285Sbrianstatic void 45137007Sbriandatalink_ComeDown(struct datalink *dl, int how) 45236285Sbrian{ 45337007Sbrian if (how != CLOSE_NORMAL) { 45444468Sbrian dl->dial.tries = -1; 45536285Sbrian dl->reconnect_tries = 0; 45637012Sbrian if (dl->state >= DATALINK_READY && how == CLOSE_LCP) 45737007Sbrian dl->stayonline = 1; 45836285Sbrian } 45936285Sbrian 46037012Sbrian if (dl->state >= DATALINK_READY && dl->stayonline) { 46137007Sbrian dl->stayonline = 0; 46237360Sbrian timer_Stop(&dl->physical->Timer); 46337007Sbrian datalink_NewState(dl, DATALINK_READY); 46437007Sbrian } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) { 46536285Sbrian modem_Offline(dl->physical); 46638414Sbrian chat_Destroy(&dl->chat); 46736285Sbrian if (dl->script.run && dl->state != DATALINK_OPENING) { 46836285Sbrian datalink_NewState(dl, DATALINK_HANGUP); 46936285Sbrian chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL); 47036285Sbrian } else 47136285Sbrian datalink_HangupDone(dl); 47236285Sbrian } 47336285Sbrian} 47436285Sbrian 47536285Sbrianstatic void 47636285Sbriandatalink_LayerStart(void *v, struct fsm *fp) 47736285Sbrian{ 47836285Sbrian /* The given FSM is about to start up ! */ 47936285Sbrian struct datalink *dl = (struct datalink *)v; 48036285Sbrian 48136285Sbrian if (fp->proto == PROTO_LCP) 48236285Sbrian (*dl->parent->LayerStart)(dl->parent->object, fp); 48336285Sbrian} 48436285Sbrian 48536285Sbrianstatic void 48636285Sbriandatalink_LayerUp(void *v, struct fsm *fp) 48736285Sbrian{ 48836285Sbrian /* The given fsm is now up */ 48936285Sbrian struct datalink *dl = (struct datalink *)v; 49044106Sbrian struct lcp *lcp = &dl->physical->link.lcp; 49136285Sbrian 49236285Sbrian if (fp->proto == PROTO_LCP) { 49343693Sbrian datalink_GotAuthname(dl, ""); 49444106Sbrian lcp->auth_ineed = lcp->want_auth; 49544106Sbrian lcp->auth_iwait = lcp->his_auth; 49644106Sbrian if (lcp->his_auth || lcp->want_auth) { 49745350Sbrian if (bundle_Phase(dl->bundle) != PHASE_NETWORK) 49836285Sbrian bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE); 49936285Sbrian log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name, 50044106Sbrian Auth2Nam(lcp->his_auth, lcp->his_authtype), 50144106Sbrian Auth2Nam(lcp->want_auth, lcp->want_authtype)); 50244106Sbrian if (lcp->his_auth == PROTO_PAP) 50343693Sbrian auth_StartReq(&dl->pap); 50444106Sbrian if (lcp->want_auth == PROTO_CHAP) 50543693Sbrian auth_StartReq(&dl->chap.auth); 50636285Sbrian } else 50736285Sbrian datalink_AuthOk(dl); 50836285Sbrian } 50936285Sbrian} 51036285Sbrian 51144094Sbrianstatic void 51244094Sbriandatalink_AuthReInit(struct datalink *dl) 51344094Sbrian{ 51444094Sbrian auth_StopTimer(&dl->pap); 51544094Sbrian auth_StopTimer(&dl->chap.auth); 51644094Sbrian chap_ReInit(&dl->chap); 51744094Sbrian} 51844094Sbrian 51936285Sbrianvoid 52043693Sbriandatalink_GotAuthname(struct datalink *dl, const char *name) 52136285Sbrian{ 52243693Sbrian strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1); 52343693Sbrian dl->peer.authname[sizeof dl->peer.authname - 1] = '\0'; 52436285Sbrian} 52536285Sbrian 52636285Sbrianvoid 52738174Sbriandatalink_NCPUp(struct datalink *dl) 52836285Sbrian{ 52937320Sbrian int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp); 53036310Sbrian 53136285Sbrian if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) { 53236285Sbrian /* we've authenticated in multilink mode ! */ 53336285Sbrian switch (mp_Up(&dl->bundle->ncp.mp, dl)) { 53436285Sbrian case MP_LINKSENT: 53536285Sbrian /* We've handed the link off to another ppp (well, we will soon) ! */ 53636285Sbrian return; 53736285Sbrian case MP_UP: 53836285Sbrian /* First link in the bundle */ 53938174Sbrian auth_Select(dl->bundle, dl->peer.authname); 54036285Sbrian /* fall through */ 54136285Sbrian case MP_ADDED: 54236285Sbrian /* We're in multilink mode ! */ 54336310Sbrian dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE; /* override */ 54436285Sbrian break; 54536285Sbrian case MP_FAILED: 54636285Sbrian datalink_AuthNotOk(dl); 54736285Sbrian return; 54836285Sbrian } 54936285Sbrian } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) { 55036285Sbrian log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name); 55137160Sbrian datalink_NewState(dl, DATALINK_OPEN); 55237160Sbrian (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm); 55336285Sbrian return; 55436285Sbrian } else { 55536285Sbrian dl->bundle->ncp.mp.peer = dl->peer; 55636285Sbrian ipcp_SetLink(&dl->bundle->ncp.ipcp, &dl->physical->link); 55738174Sbrian auth_Select(dl->bundle, dl->peer.authname); 55836285Sbrian } 55936285Sbrian 56037320Sbrian if (ccpok) { 56137320Sbrian fsm_Up(&dl->physical->link.ccp.fsm); 56237320Sbrian fsm_Open(&dl->physical->link.ccp.fsm); 56337320Sbrian } 56436285Sbrian datalink_NewState(dl, DATALINK_OPEN); 56536285Sbrian bundle_NewPhase(dl->bundle, PHASE_NETWORK); 56636285Sbrian (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm); 56736285Sbrian} 56836285Sbrian 56936285Sbrianvoid 57038174Sbriandatalink_CBCPComplete(struct datalink *dl) 57138174Sbrian{ 57238174Sbrian datalink_NewState(dl, DATALINK_LCP); 57344094Sbrian datalink_AuthReInit(dl); 57438174Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 57538174Sbrian} 57638174Sbrian 57738174Sbrianvoid 57838174Sbriandatalink_CBCPFailed(struct datalink *dl) 57938174Sbrian{ 58038174Sbrian cbcp_Down(&dl->cbcp); 58138174Sbrian datalink_CBCPComplete(dl); 58238174Sbrian} 58338174Sbrian 58438174Sbrianvoid 58538174Sbriandatalink_AuthOk(struct datalink *dl) 58638174Sbrian{ 58742600Sbrian if ((dl->physical->link.lcp.his_callback.opmask & 58842600Sbrian CALLBACK_BIT(CALLBACK_CBCP) || 58942600Sbrian dl->physical->link.lcp.want_callback.opmask & 59042600Sbrian CALLBACK_BIT(CALLBACK_CBCP)) && 59142600Sbrian !(dl->physical->link.lcp.want_callback.opmask & 59242600Sbrian CALLBACK_BIT(CALLBACK_AUTH))) { 59342600Sbrian /* We must have agreed CBCP if AUTH isn't there any more */ 59438174Sbrian datalink_NewState(dl, DATALINK_CBCP); 59538174Sbrian cbcp_Up(&dl->cbcp); 59638174Sbrian } else if (dl->physical->link.lcp.want_callback.opmask) { 59742600Sbrian /* It's not CBCP */ 59838174Sbrian log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name); 59938174Sbrian datalink_NewState(dl, DATALINK_LCP); 60044094Sbrian datalink_AuthReInit(dl); 60138174Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 60238174Sbrian } else 60338174Sbrian switch (dl->physical->link.lcp.his_callback.opmask) { 60438174Sbrian case 0: 60538174Sbrian datalink_NCPUp(dl); 60638174Sbrian break; 60738174Sbrian 60838174Sbrian case CALLBACK_BIT(CALLBACK_AUTH): 60938174Sbrian auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone, 61038174Sbrian sizeof dl->cbcp.fsm.phone); 61138174Sbrian if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) { 61238174Sbrian log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name, 61338174Sbrian dl->peer.authname); 61438174Sbrian *dl->cbcp.fsm.phone = '\0'; 61538174Sbrian } else { 61638174Sbrian char *ptr = strchr(dl->cbcp.fsm.phone, ','); 61738174Sbrian if (ptr) 61838174Sbrian *ptr = '\0'; /* Call back on the first number */ 61938174Sbrian log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name, 62038174Sbrian dl->cbcp.fsm.phone); 62138174Sbrian dl->cbcp.required = 1; 62238174Sbrian } 62338174Sbrian dl->cbcp.fsm.delay = 0; 62438174Sbrian datalink_NewState(dl, DATALINK_LCP); 62544094Sbrian datalink_AuthReInit(dl); 62638174Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 62738174Sbrian break; 62838174Sbrian 62938174Sbrian case CALLBACK_BIT(CALLBACK_E164): 63038174Sbrian strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg, 63138174Sbrian sizeof dl->cbcp.fsm.phone - 1); 63238174Sbrian dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0'; 63338174Sbrian log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name, 63438174Sbrian dl->cbcp.fsm.phone); 63538174Sbrian dl->cbcp.required = 1; 63638174Sbrian dl->cbcp.fsm.delay = 0; 63738174Sbrian datalink_NewState(dl, DATALINK_LCP); 63844094Sbrian datalink_AuthReInit(dl); 63938174Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 64038174Sbrian break; 64138174Sbrian 64238174Sbrian default: 64338174Sbrian log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n", 64438174Sbrian dl->name); 64538174Sbrian datalink_NewState(dl, DATALINK_LCP); 64644094Sbrian datalink_AuthReInit(dl); 64738174Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 64838174Sbrian break; 64938174Sbrian } 65038174Sbrian} 65138174Sbrian 65238174Sbrianvoid 65336285Sbriandatalink_AuthNotOk(struct datalink *dl) 65436285Sbrian{ 65536285Sbrian datalink_NewState(dl, DATALINK_LCP); 65644094Sbrian datalink_AuthReInit(dl); 65736285Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 65836285Sbrian} 65936285Sbrian 66036285Sbrianstatic void 66136285Sbriandatalink_LayerDown(void *v, struct fsm *fp) 66236285Sbrian{ 66336285Sbrian /* The given FSM has been told to come down */ 66436285Sbrian struct datalink *dl = (struct datalink *)v; 66536285Sbrian 66636285Sbrian if (fp->proto == PROTO_LCP) { 66736285Sbrian switch (dl->state) { 66836285Sbrian case DATALINK_OPEN: 66936285Sbrian peerid_Init(&dl->peer); 67037060Sbrian fsm2initial(&dl->physical->link.ccp.fsm); 67136928Sbrian datalink_NewState(dl, DATALINK_LCP); /* before parent TLD */ 67236285Sbrian (*dl->parent->LayerDown)(dl->parent->object, fp); 67338174Sbrian /* fall through (just in case) */ 67436285Sbrian 67538174Sbrian case DATALINK_CBCP: 67638174Sbrian if (!dl->cbcp.required) 67738174Sbrian cbcp_Down(&dl->cbcp); 67838174Sbrian /* fall through (just in case) */ 67938174Sbrian 68036285Sbrian case DATALINK_AUTH: 68136285Sbrian timer_Stop(&dl->pap.authtimer); 68236285Sbrian timer_Stop(&dl->chap.auth.authtimer); 68336285Sbrian } 68436285Sbrian datalink_NewState(dl, DATALINK_LCP); 68544094Sbrian datalink_AuthReInit(dl); 68636285Sbrian } 68736285Sbrian} 68836285Sbrian 68936285Sbrianstatic void 69036285Sbriandatalink_LayerFinish(void *v, struct fsm *fp) 69136285Sbrian{ 69236285Sbrian /* The given fsm is now down */ 69336285Sbrian struct datalink *dl = (struct datalink *)v; 69436285Sbrian 69536285Sbrian if (fp->proto == PROTO_LCP) { 69637060Sbrian fsm2initial(fp); 69736285Sbrian (*dl->parent->LayerFinish)(dl->parent->object, fp); 69837007Sbrian datalink_ComeDown(dl, CLOSE_NORMAL); 69936285Sbrian } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE) 70036285Sbrian fsm_Open(fp); /* CCP goes to ST_STOPPED */ 70136285Sbrian} 70236285Sbrian 70336285Sbrianstruct datalink * 70436285Sbriandatalink_Create(const char *name, struct bundle *bundle, int type) 70536285Sbrian{ 70636285Sbrian struct datalink *dl; 70736285Sbrian 70836285Sbrian dl = (struct datalink *)malloc(sizeof(struct datalink)); 70936285Sbrian if (dl == NULL) 71036285Sbrian return dl; 71136285Sbrian 71236285Sbrian dl->desc.type = DATALINK_DESCRIPTOR; 71336285Sbrian dl->desc.UpdateSet = datalink_UpdateSet; 71436285Sbrian dl->desc.IsSet = datalink_IsSet; 71536285Sbrian dl->desc.Read = datalink_Read; 71636285Sbrian dl->desc.Write = datalink_Write; 71736285Sbrian 71836285Sbrian dl->state = DATALINK_CLOSED; 71936285Sbrian 72036285Sbrian *dl->cfg.script.dial = '\0'; 72136285Sbrian *dl->cfg.script.login = '\0'; 72236285Sbrian *dl->cfg.script.hangup = '\0'; 72336285Sbrian *dl->cfg.phone.list = '\0'; 72436285Sbrian *dl->phone.list = '\0'; 72536285Sbrian dl->phone.next = NULL; 72636285Sbrian dl->phone.alt = NULL; 72736285Sbrian dl->phone.chosen = "N/A"; 72837007Sbrian dl->stayonline = 0; 72936285Sbrian dl->script.run = 1; 73036285Sbrian dl->script.packetmode = 1; 73136285Sbrian mp_linkInit(&dl->mp); 73236285Sbrian 73336285Sbrian dl->bundle = bundle; 73436285Sbrian dl->next = NULL; 73536285Sbrian 73644468Sbrian memset(&dl->dial.timer, '\0', sizeof dl->dial.timer); 73736285Sbrian 73844468Sbrian dl->dial.tries = 0; 73936285Sbrian dl->cfg.dial.max = 1; 74036285Sbrian dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT; 74136285Sbrian dl->cfg.dial.timeout = DIAL_TIMEOUT; 74244468Sbrian dl->cfg.dial.inc = 0; 74344468Sbrian dl->cfg.dial.maxinc = 10; 74436285Sbrian 74536285Sbrian dl->reconnect_tries = 0; 74636285Sbrian dl->cfg.reconnect.max = 0; 74736285Sbrian dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT; 74836285Sbrian 74938174Sbrian dl->cfg.callback.opmask = 0; 75038174Sbrian dl->cfg.cbcp.delay = 0; 75138174Sbrian *dl->cfg.cbcp.phone = '\0'; 75238174Sbrian dl->cfg.cbcp.fsmretry = DEF_FSMRETRY; 75338174Sbrian 75436285Sbrian dl->name = strdup(name); 75536285Sbrian peerid_Init(&dl->peer); 75636285Sbrian dl->parent = &bundle->fsm; 75736285Sbrian dl->fsmp.LayerStart = datalink_LayerStart; 75836285Sbrian dl->fsmp.LayerUp = datalink_LayerUp; 75936285Sbrian dl->fsmp.LayerDown = datalink_LayerDown; 76036285Sbrian dl->fsmp.LayerFinish = datalink_LayerFinish; 76136285Sbrian dl->fsmp.object = dl; 76236285Sbrian 76336285Sbrian if ((dl->physical = modem_Create(dl, type)) == NULL) { 76436285Sbrian free(dl->name); 76536285Sbrian free(dl); 76636285Sbrian return NULL; 76736285Sbrian } 76843693Sbrian 76943693Sbrian pap_Init(&dl->pap, dl->physical); 77043693Sbrian chap_Init(&dl->chap, dl->physical); 77138174Sbrian cbcp_Init(&dl->cbcp, dl->physical); 77236285Sbrian chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); 77336285Sbrian 77436285Sbrian log_Printf(LogPHASE, "%s: Created in %s state\n", 77536285Sbrian dl->name, datalink_State(dl)); 77636285Sbrian 77736285Sbrian return dl; 77836285Sbrian} 77936285Sbrian 78036285Sbrianstruct datalink * 78136285Sbriandatalink_Clone(struct datalink *odl, const char *name) 78236285Sbrian{ 78336285Sbrian struct datalink *dl; 78436285Sbrian 78536285Sbrian dl = (struct datalink *)malloc(sizeof(struct datalink)); 78636285Sbrian if (dl == NULL) 78736285Sbrian return dl; 78836285Sbrian 78936285Sbrian dl->desc.type = DATALINK_DESCRIPTOR; 79036285Sbrian dl->desc.UpdateSet = datalink_UpdateSet; 79136285Sbrian dl->desc.IsSet = datalink_IsSet; 79236285Sbrian dl->desc.Read = datalink_Read; 79336285Sbrian dl->desc.Write = datalink_Write; 79436285Sbrian 79536285Sbrian dl->state = DATALINK_CLOSED; 79636285Sbrian 79736285Sbrian memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg); 79836285Sbrian mp_linkInit(&dl->mp); 79936285Sbrian *dl->phone.list = '\0'; 80036285Sbrian dl->phone.next = NULL; 80136285Sbrian dl->phone.alt = NULL; 80236285Sbrian dl->phone.chosen = "N/A"; 80336285Sbrian dl->bundle = odl->bundle; 80436285Sbrian dl->next = NULL; 80544468Sbrian memset(&dl->dial.timer, '\0', sizeof dl->dial.timer); 80644468Sbrian dl->dial.tries = 0; 80736285Sbrian dl->reconnect_tries = 0; 80836285Sbrian dl->name = strdup(name); 80936285Sbrian peerid_Init(&dl->peer); 81036285Sbrian dl->parent = odl->parent; 81136285Sbrian memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp); 81236285Sbrian dl->fsmp.object = dl; 81336285Sbrian 81436465Sbrian if ((dl->physical = modem_Create(dl, PHYS_INTERACTIVE)) == NULL) { 81536285Sbrian free(dl->name); 81636285Sbrian free(dl); 81736285Sbrian return NULL; 81836285Sbrian } 81943693Sbrian pap_Init(&dl->pap, dl->physical); 82044305Sbrian dl->pap.cfg = odl->pap.cfg; 82143693Sbrian 82243693Sbrian chap_Init(&dl->chap, dl->physical); 82344305Sbrian dl->chap.auth.cfg = odl->chap.auth.cfg; 82443693Sbrian 82536285Sbrian memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg); 82636285Sbrian memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg, 82736285Sbrian sizeof dl->physical->link.lcp.cfg); 82836285Sbrian memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg, 82936285Sbrian sizeof dl->physical->link.ccp.cfg); 83036285Sbrian memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg, 83136285Sbrian sizeof dl->physical->async.cfg); 83236285Sbrian 83338174Sbrian cbcp_Init(&dl->cbcp, dl->physical); 83436285Sbrian chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); 83536285Sbrian 83636285Sbrian log_Printf(LogPHASE, "%s: Cloned in %s state\n", 83736285Sbrian dl->name, datalink_State(dl)); 83836285Sbrian 83936285Sbrian return dl; 84036285Sbrian} 84136285Sbrian 84236285Sbrianstruct datalink * 84336285Sbriandatalink_Destroy(struct datalink *dl) 84436285Sbrian{ 84536285Sbrian struct datalink *result; 84636285Sbrian 84736285Sbrian if (dl->state != DATALINK_CLOSED) { 84836285Sbrian log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n", 84936285Sbrian datalink_State(dl)); 85036285Sbrian switch (dl->state) { 85136285Sbrian case DATALINK_HANGUP: 85236285Sbrian case DATALINK_DIAL: 85336285Sbrian case DATALINK_LOGIN: 85436285Sbrian chat_Destroy(&dl->chat); /* Gotta blat the timers ! */ 85536285Sbrian break; 85636285Sbrian } 85736285Sbrian } 85836285Sbrian 85944468Sbrian timer_Stop(&dl->dial.timer); 86036285Sbrian result = dl->next; 86136285Sbrian modem_Destroy(dl->physical); 86236285Sbrian free(dl->name); 86336285Sbrian free(dl); 86436285Sbrian 86536285Sbrian return result; 86636285Sbrian} 86736285Sbrian 86836285Sbrianvoid 86936285Sbriandatalink_Up(struct datalink *dl, int runscripts, int packetmode) 87036285Sbrian{ 87136285Sbrian if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED)) 87236285Sbrian /* Ignore scripts */ 87336285Sbrian runscripts = 0; 87436285Sbrian 87536285Sbrian switch (dl->state) { 87636285Sbrian case DATALINK_CLOSED: 87736285Sbrian if (bundle_Phase(dl->bundle) == PHASE_DEAD || 87836285Sbrian bundle_Phase(dl->bundle) == PHASE_TERMINATE) 87936285Sbrian bundle_NewPhase(dl->bundle, PHASE_ESTABLISH); 88036285Sbrian datalink_NewState(dl, DATALINK_OPENING); 88136285Sbrian dl->reconnect_tries = 88236285Sbrian dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max; 88344468Sbrian dl->dial.tries = dl->cfg.dial.max; 88436285Sbrian dl->script.run = runscripts; 88536285Sbrian dl->script.packetmode = packetmode; 88636285Sbrian break; 88736285Sbrian 88836285Sbrian case DATALINK_OPENING: 88936285Sbrian if (!dl->script.run && runscripts) 89036285Sbrian dl->script.run = 1; 89136285Sbrian /* fall through */ 89236285Sbrian 89336285Sbrian case DATALINK_DIAL: 89436285Sbrian case DATALINK_LOGIN: 89536285Sbrian case DATALINK_READY: 89636285Sbrian if (!dl->script.packetmode && packetmode) { 89736285Sbrian dl->script.packetmode = 1; 89836285Sbrian if (dl->state == DATALINK_READY) 89936285Sbrian datalink_LoginDone(dl); 90036285Sbrian } 90136285Sbrian break; 90236285Sbrian } 90336285Sbrian} 90436285Sbrian 90536285Sbrianvoid 90637007Sbriandatalink_Close(struct datalink *dl, int how) 90736285Sbrian{ 90836285Sbrian /* Please close */ 90936285Sbrian switch (dl->state) { 91036285Sbrian case DATALINK_OPEN: 91136285Sbrian peerid_Init(&dl->peer); 91237060Sbrian fsm2initial(&dl->physical->link.ccp.fsm); 91336285Sbrian /* fall through */ 91436285Sbrian 91538174Sbrian case DATALINK_CBCP: 91636285Sbrian case DATALINK_AUTH: 91736285Sbrian case DATALINK_LCP: 91844094Sbrian datalink_AuthReInit(dl); 91936285Sbrian fsm_Close(&dl->physical->link.lcp.fsm); 92037007Sbrian if (how != CLOSE_NORMAL) { 92144468Sbrian dl->dial.tries = -1; 92236285Sbrian dl->reconnect_tries = 0; 92337007Sbrian if (how == CLOSE_LCP) 92437007Sbrian dl->stayonline = 1; 92536285Sbrian } 92636285Sbrian break; 92736285Sbrian 92836285Sbrian default: 92937007Sbrian datalink_ComeDown(dl, how); 93036285Sbrian } 93136285Sbrian} 93236285Sbrian 93336285Sbrianvoid 93437007Sbriandatalink_Down(struct datalink *dl, int how) 93536285Sbrian{ 93636285Sbrian /* Carrier is lost */ 93736285Sbrian switch (dl->state) { 93836285Sbrian case DATALINK_OPEN: 93936285Sbrian peerid_Init(&dl->peer); 94037060Sbrian fsm2initial(&dl->physical->link.ccp.fsm); 94136285Sbrian /* fall through */ 94236285Sbrian 94338174Sbrian case DATALINK_CBCP: 94436285Sbrian case DATALINK_AUTH: 94536285Sbrian case DATALINK_LCP: 94637060Sbrian fsm2initial(&dl->physical->link.lcp.fsm); 94736285Sbrian /* fall through */ 94836285Sbrian 94936285Sbrian default: 95037007Sbrian datalink_ComeDown(dl, how); 95136285Sbrian } 95236285Sbrian} 95336285Sbrian 95436285Sbrianvoid 95536285Sbriandatalink_StayDown(struct datalink *dl) 95636285Sbrian{ 95736285Sbrian dl->reconnect_tries = 0; 95836285Sbrian} 95936285Sbrian 96037007Sbrianvoid 96137007Sbriandatalink_DontHangup(struct datalink *dl) 96237007Sbrian{ 96337012Sbrian if (dl->state >= DATALINK_LCP) 96437012Sbrian dl->stayonline = 1; 96537007Sbrian} 96637007Sbrian 96736285Sbrianint 96836285Sbriandatalink_Show(struct cmdargs const *arg) 96936285Sbrian{ 97036285Sbrian prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name); 97138174Sbrian prompt_Printf(arg->prompt, " State: %s\n", 97236285Sbrian datalink_State(arg->cx)); 97338174Sbrian prompt_Printf(arg->prompt, " Peer name: "); 97436285Sbrian if (*arg->cx->peer.authname) 97536285Sbrian prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname); 97636285Sbrian else if (arg->cx->state == DATALINK_OPEN) 97736285Sbrian prompt_Printf(arg->prompt, "None requested\n"); 97836285Sbrian else 97936285Sbrian prompt_Printf(arg->prompt, "N/A\n"); 98038174Sbrian prompt_Printf(arg->prompt, " Discriminator: %s\n", 98136285Sbrian mp_Enddisc(arg->cx->peer.enddisc.class, 98236285Sbrian arg->cx->peer.enddisc.address, 98336285Sbrian arg->cx->peer.enddisc.len)); 98436285Sbrian 98536285Sbrian prompt_Printf(arg->prompt, "\nDefaults:\n"); 98638174Sbrian prompt_Printf(arg->prompt, " Phone List: %s\n", 98736285Sbrian arg->cx->cfg.phone.list); 98836285Sbrian if (arg->cx->cfg.dial.max) 98938174Sbrian prompt_Printf(arg->prompt, " Dial tries: %d, delay ", 99036285Sbrian arg->cx->cfg.dial.max); 99136285Sbrian else 99238174Sbrian prompt_Printf(arg->prompt, " Dial tries: infinite, delay "); 99344261Sbrian if (arg->cx->cfg.dial.next_timeout >= 0) 99436285Sbrian prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout); 99536285Sbrian else 99636285Sbrian prompt_Printf(arg->prompt, "random/"); 99744261Sbrian if (arg->cx->cfg.dial.timeout >= 0) 99836285Sbrian prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout); 99936285Sbrian else 100036285Sbrian prompt_Printf(arg->prompt, "random\n"); 100138174Sbrian prompt_Printf(arg->prompt, " Reconnect tries: %d, delay ", 100236285Sbrian arg->cx->cfg.reconnect.max); 100336285Sbrian if (arg->cx->cfg.reconnect.timeout > 0) 100436285Sbrian prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout); 100536285Sbrian else 100636285Sbrian prompt_Printf(arg->prompt, "random\n"); 100738174Sbrian prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type == 100838174Sbrian PHYS_DIRECT ? "accepted: " : "requested:"); 100938174Sbrian if (!arg->cx->cfg.callback.opmask) 101038174Sbrian prompt_Printf(arg->prompt, "none\n"); 101138174Sbrian else { 101238174Sbrian int comma = 0; 101338174Sbrian 101438174Sbrian if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) { 101538174Sbrian prompt_Printf(arg->prompt, "none"); 101638174Sbrian comma = 1; 101738174Sbrian } 101838174Sbrian if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) { 101938174Sbrian prompt_Printf(arg->prompt, "%sauth", comma ? ", " : ""); 102038174Sbrian comma = 1; 102138174Sbrian } 102238174Sbrian if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) { 102338174Sbrian prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : ""); 102438174Sbrian if (arg->cx->physical->type != PHYS_DIRECT) 102538174Sbrian prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg); 102638174Sbrian comma = 1; 102738174Sbrian } 102838174Sbrian if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) { 102938174Sbrian prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : ""); 103038174Sbrian prompt_Printf(arg->prompt, " CBCP: delay: %ds\n", 103138174Sbrian arg->cx->cfg.cbcp.delay); 103240483Sbrian prompt_Printf(arg->prompt, " phone: "); 103340483Sbrian if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) { 103440483Sbrian if (arg->cx->physical->type & PHYS_DIRECT) 103540483Sbrian prompt_Printf(arg->prompt, "Caller decides\n"); 103640483Sbrian else 103740483Sbrian prompt_Printf(arg->prompt, "Dialback server decides\n"); 103840483Sbrian } else 103940483Sbrian prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone); 104038174Sbrian prompt_Printf(arg->prompt, " timeout: %lds\n", 104138174Sbrian arg->cx->cfg.cbcp.fsmretry); 104238174Sbrian } else 104338174Sbrian prompt_Printf(arg->prompt, "\n"); 104438174Sbrian } 104538174Sbrian 104638174Sbrian prompt_Printf(arg->prompt, " Dial Script: %s\n", 104736285Sbrian arg->cx->cfg.script.dial); 104838174Sbrian prompt_Printf(arg->prompt, " Login Script: %s\n", 104936285Sbrian arg->cx->cfg.script.login); 105038174Sbrian prompt_Printf(arg->prompt, " Hangup Script: %s\n", 105136285Sbrian arg->cx->cfg.script.hangup); 105236285Sbrian return 0; 105336285Sbrian} 105436285Sbrian 105536285Sbrianint 105636285Sbriandatalink_SetReconnect(struct cmdargs const *arg) 105736285Sbrian{ 105836285Sbrian if (arg->argc == arg->argn+2) { 105936285Sbrian arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]); 106036285Sbrian arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]); 106136285Sbrian return 0; 106236285Sbrian } 106336285Sbrian return -1; 106436285Sbrian} 106536285Sbrian 106636285Sbrianint 106736285Sbriandatalink_SetRedial(struct cmdargs const *arg) 106836285Sbrian{ 106944468Sbrian const char *sep, *osep; 107044468Sbrian int timeout, inc, maxinc, tries; 107136285Sbrian 107236285Sbrian if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) { 107336285Sbrian if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 && 107436285Sbrian (arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) { 107536285Sbrian arg->cx->cfg.dial.timeout = -1; 107636285Sbrian randinit(); 107736285Sbrian } else { 107836285Sbrian timeout = atoi(arg->argv[arg->argn]); 107936285Sbrian 108036285Sbrian if (timeout >= 0) 108136285Sbrian arg->cx->cfg.dial.timeout = timeout; 108236285Sbrian else { 108336285Sbrian log_Printf(LogWARN, "Invalid redial timeout\n"); 108436285Sbrian return -1; 108536285Sbrian } 108636285Sbrian } 108736285Sbrian 108844468Sbrian sep = strchr(arg->argv[arg->argn], '+'); 108944468Sbrian if (sep) { 109044468Sbrian inc = atoi(++sep); 109144468Sbrian osep = sep; 109244468Sbrian if (inc >= 0) 109344468Sbrian arg->cx->cfg.dial.inc = inc; 109444468Sbrian else { 109544468Sbrian log_Printf(LogWARN, "Invalid timeout increment\n"); 109644468Sbrian return -1; 109744468Sbrian } 109844468Sbrian sep = strchr(sep, '-'); 109944468Sbrian if (sep) { 110044468Sbrian maxinc = atoi(++sep); 110144468Sbrian if (maxinc >= 0) 110244468Sbrian arg->cx->cfg.dial.maxinc = maxinc; 110344468Sbrian else { 110444468Sbrian log_Printf(LogWARN, "Invalid maximum timeout increments\n"); 110544468Sbrian return -1; 110644468Sbrian } 110744468Sbrian } else { 110844468Sbrian /* Default timeout increment */ 110944468Sbrian arg->cx->cfg.dial.maxinc = 10; 111044468Sbrian sep = osep; 111144468Sbrian } 111244468Sbrian } else { 111344468Sbrian /* Default timeout increment & max increment */ 111444468Sbrian arg->cx->cfg.dial.inc = 0; 111544468Sbrian arg->cx->cfg.dial.maxinc = 10; 111644468Sbrian sep = arg->argv[arg->argn]; 111744468Sbrian } 111844468Sbrian 111944468Sbrian sep = strchr(sep, '.'); 112044468Sbrian if (sep) { 112144468Sbrian if (strcasecmp(++sep, "random") == 0) { 112236285Sbrian arg->cx->cfg.dial.next_timeout = -1; 112336285Sbrian randinit(); 112436285Sbrian } else { 112544468Sbrian timeout = atoi(sep); 112636285Sbrian if (timeout >= 0) 112736285Sbrian arg->cx->cfg.dial.next_timeout = timeout; 112836285Sbrian else { 112936285Sbrian log_Printf(LogWARN, "Invalid next redial timeout\n"); 113036285Sbrian return -1; 113136285Sbrian } 113236285Sbrian } 113336285Sbrian } else 113436285Sbrian /* Default next timeout */ 113536285Sbrian arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT; 113636285Sbrian 113736285Sbrian if (arg->argc == arg->argn+2) { 113836285Sbrian tries = atoi(arg->argv[arg->argn+1]); 113936285Sbrian 114036285Sbrian if (tries >= 0) { 114136285Sbrian arg->cx->cfg.dial.max = tries; 114236285Sbrian } else { 114336285Sbrian log_Printf(LogWARN, "Invalid retry value\n"); 114436285Sbrian return 1; 114536285Sbrian } 114636285Sbrian } 114736285Sbrian return 0; 114836285Sbrian } 114944468Sbrian 115036285Sbrian return -1; 115136285Sbrian} 115236285Sbrian 115336285Sbrianstatic const char *states[] = { 115436285Sbrian "closed", 115536285Sbrian "opening", 115636285Sbrian "hangup", 115736285Sbrian "dial", 115836285Sbrian "login", 115936285Sbrian "ready", 116036285Sbrian "lcp", 116136285Sbrian "auth", 116238174Sbrian "cbcp", 116336285Sbrian "open" 116436285Sbrian}; 116536285Sbrian 116636285Sbrianconst char * 116736285Sbriandatalink_State(struct datalink *dl) 116836285Sbrian{ 116936285Sbrian if (dl->state < 0 || dl->state >= sizeof states / sizeof states[0]) 117036285Sbrian return "unknown"; 117136285Sbrian return states[dl->state]; 117236285Sbrian} 117336285Sbrian 117436285Sbrianstatic void 117536285Sbriandatalink_NewState(struct datalink *dl, int state) 117636285Sbrian{ 117736285Sbrian if (state != dl->state) { 117836285Sbrian if (state >= 0 && state < sizeof states / sizeof states[0]) { 117936285Sbrian log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl), 118036285Sbrian states[state]); 118136285Sbrian dl->state = state; 118236285Sbrian } else 118336285Sbrian log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state); 118436285Sbrian } 118536285Sbrian} 118636285Sbrian 118736285Sbrianstruct datalink * 118836285Sbrianiov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov, 118936285Sbrian int fd) 119036285Sbrian{ 119136285Sbrian struct datalink *dl, *cdl; 119244305Sbrian struct fsm_retry copy; 119336285Sbrian char *oname; 119436285Sbrian 119536285Sbrian dl = (struct datalink *)iov[(*niov)++].iov_base; 119636285Sbrian dl->name = iov[*niov].iov_base; 119736285Sbrian 119836285Sbrian if (dl->name[DATALINK_MAXNAME-1]) { 119936285Sbrian dl->name[DATALINK_MAXNAME-1] = '\0'; 120036285Sbrian if (strlen(dl->name) == DATALINK_MAXNAME - 1) 120136285Sbrian log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name); 120236285Sbrian } 120336285Sbrian 120436285Sbrian /* Make sure the name is unique ! */ 120536285Sbrian oname = NULL; 120636285Sbrian do { 120736285Sbrian for (cdl = bundle->links; cdl; cdl = cdl->next) 120836285Sbrian if (!strcasecmp(dl->name, cdl->name)) { 120936285Sbrian if (oname) 121036285Sbrian free(datalink_NextName(dl)); 121136285Sbrian else 121236285Sbrian oname = datalink_NextName(dl); 121336285Sbrian break; /* Keep renaming 'till we have no conflicts */ 121436285Sbrian } 121536285Sbrian } while (cdl); 121636285Sbrian 121736285Sbrian if (oname) { 121836285Sbrian log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name); 121936285Sbrian free(oname); 122036285Sbrian } else { 122136285Sbrian dl->name = strdup(dl->name); 122236285Sbrian free(iov[*niov].iov_base); 122336285Sbrian } 122436285Sbrian (*niov)++; 122536285Sbrian 122636285Sbrian dl->desc.type = DATALINK_DESCRIPTOR; 122736285Sbrian dl->desc.UpdateSet = datalink_UpdateSet; 122836285Sbrian dl->desc.IsSet = datalink_IsSet; 122936285Sbrian dl->desc.Read = datalink_Read; 123036285Sbrian dl->desc.Write = datalink_Write; 123136285Sbrian 123236285Sbrian mp_linkInit(&dl->mp); 123336285Sbrian *dl->phone.list = '\0'; 123436285Sbrian dl->phone.next = NULL; 123536285Sbrian dl->phone.alt = NULL; 123636285Sbrian dl->phone.chosen = "N/A"; 123736285Sbrian 123836285Sbrian dl->bundle = bundle; 123936285Sbrian dl->next = NULL; 124044468Sbrian memset(&dl->dial.timer, '\0', sizeof dl->dial.timer); 124144468Sbrian dl->dial.tries = 0; 124236285Sbrian dl->reconnect_tries = 0; 124336285Sbrian dl->parent = &bundle->fsm; 124436285Sbrian dl->fsmp.LayerStart = datalink_LayerStart; 124536285Sbrian dl->fsmp.LayerUp = datalink_LayerUp; 124636285Sbrian dl->fsmp.LayerDown = datalink_LayerDown; 124736285Sbrian dl->fsmp.LayerFinish = datalink_LayerFinish; 124836285Sbrian dl->fsmp.object = dl; 124936285Sbrian 125036285Sbrian dl->physical = iov2modem(dl, iov, niov, maxiov, fd); 125136285Sbrian 125236285Sbrian if (!dl->physical) { 125336285Sbrian free(dl->name); 125436285Sbrian free(dl); 125536285Sbrian dl = NULL; 125636285Sbrian } else { 125744305Sbrian copy = dl->pap.cfg.fsm; 125843693Sbrian pap_Init(&dl->pap, dl->physical); 125944305Sbrian dl->pap.cfg.fsm = copy; 126043693Sbrian 126144305Sbrian copy = dl->chap.auth.cfg.fsm; 126243693Sbrian chap_Init(&dl->chap, dl->physical); 126344305Sbrian dl->chap.auth.cfg.fsm = copy; 126443693Sbrian 126538174Sbrian cbcp_Init(&dl->cbcp, dl->physical); 126636285Sbrian chat_Init(&dl->chat, dl->physical, NULL, 1, NULL); 126736285Sbrian 126836285Sbrian log_Printf(LogPHASE, "%s: Transferred in %s state\n", 126936285Sbrian dl->name, datalink_State(dl)); 127036285Sbrian } 127136285Sbrian 127236285Sbrian return dl; 127336285Sbrian} 127436285Sbrian 127536285Sbrianint 127636450Sbriandatalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov, 127736450Sbrian pid_t newpid) 127836285Sbrian{ 127936285Sbrian /* If `dl' is NULL, we're allocating before a Fromiov() */ 128036285Sbrian int link_fd; 128136285Sbrian 128236285Sbrian if (dl) { 128344468Sbrian timer_Stop(&dl->dial.timer); 128438174Sbrian /* The following is purely for the sake of paranoia */ 128538174Sbrian cbcp_Down(&dl->cbcp); 128636285Sbrian timer_Stop(&dl->pap.authtimer); 128736285Sbrian timer_Stop(&dl->chap.auth.authtimer); 128836285Sbrian } 128936285Sbrian 129036285Sbrian if (*niov >= maxiov - 1) { 129136285Sbrian log_Printf(LogERROR, "Toiov: No room for datalink !\n"); 129236285Sbrian if (dl) { 129336285Sbrian free(dl->name); 129436285Sbrian free(dl); 129536285Sbrian } 129636285Sbrian return -1; 129736285Sbrian } 129836285Sbrian 129936285Sbrian iov[*niov].iov_base = dl ? dl : malloc(sizeof *dl); 130036285Sbrian iov[(*niov)++].iov_len = sizeof *dl; 130136285Sbrian iov[*niov].iov_base = 130236285Sbrian dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME); 130336285Sbrian iov[(*niov)++].iov_len = DATALINK_MAXNAME; 130436285Sbrian 130536450Sbrian link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid); 130636285Sbrian 130736285Sbrian if (link_fd == -1 && dl) { 130836285Sbrian free(dl->name); 130936285Sbrian free(dl); 131036285Sbrian } 131136285Sbrian 131236285Sbrian return link_fd; 131336285Sbrian} 131436285Sbrian 131536285Sbrianvoid 131636285Sbriandatalink_Rename(struct datalink *dl, const char *name) 131736285Sbrian{ 131836285Sbrian free(dl->name); 131936285Sbrian dl->physical->link.name = dl->name = strdup(name); 132036285Sbrian} 132136285Sbrian 132236285Sbrianchar * 132336285Sbriandatalink_NextName(struct datalink *dl) 132436285Sbrian{ 132536285Sbrian int f, n; 132636285Sbrian char *name, *oname; 132736285Sbrian 132836285Sbrian n = strlen(dl->name); 132936285Sbrian name = (char *)malloc(n+3); 133036285Sbrian for (f = n - 1; f >= 0; f--) 133136285Sbrian if (!isdigit(dl->name[f])) 133236285Sbrian break; 133336285Sbrian n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name); 133436285Sbrian sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1); 133536285Sbrian oname = dl->name; 133636345Sbrian dl->name = name; 133736345Sbrian /* our physical link name isn't updated (it probably isn't created yet) */ 133836285Sbrian return oname; 133936285Sbrian} 134036285Sbrian 134136285Sbrianint 134236285Sbriandatalink_SetMode(struct datalink *dl, int mode) 134336285Sbrian{ 134436285Sbrian if (!physical_SetMode(dl->physical, mode)) 134536285Sbrian return 0; 134636285Sbrian if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED)) 134736285Sbrian dl->script.run = 0; 134836285Sbrian if (dl->physical->type == PHYS_DIRECT) 134936285Sbrian dl->reconnect_tries = 0; 135036465Sbrian if (mode & (PHYS_DDIAL|PHYS_BACKGROUND) && dl->state <= DATALINK_READY) 135136285Sbrian datalink_Up(dl, 1, 1); 135236285Sbrian return 1; 135336285Sbrian} 135444468Sbrian 135544468Sbrianint 135644468Sbriandatalink_GetDialTimeout(struct datalink *dl) 135744468Sbrian{ 135844468Sbrian int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc; 135944468Sbrian 136044468Sbrian if (dl->dial.incs < dl->cfg.dial.maxinc) 136144468Sbrian dl->dial.incs++; 136244468Sbrian 136344468Sbrian return result; 136444468Sbrian} 1365