freebsd-dev/usr.sbin/ppp/modem.c
Poul-Henning Kamp 9c749ffb8b Here is a diff of /usr/src/usr.sbin/ppp against current. The diffs
add some logging functionality which I find very useful.
'set debug link' will record just link up/down and address assignments.
'set debug connect' will record the entire chat dialog
'set debug carrier' will record just chat lines including 'CARRIER'
(so that I can be sure I'm getting a 28.8 line).

There was a global change required to permit LogPrintf to take a bit
mask instead of a bit position value (to permit logging some events
on either of two flags, so that no change in 'set debug lcp' would
result from the code supporting 'link'.  Thus the diffs are rather
long for such a small change.  The man page is also touched.

Oh, and there was a slight syntax problem in route.c

Reviewed by:	phk
Submitted by:	Tony Kimball <alk@Think.COM>
1996-05-11 20:48:42 +00:00

794 lines
16 KiB
C

/*
* PPP Modem handling module
*
* Written by Toshiharu OHNO (tony-o@iij.ad.jp)
*
* Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the Internet Initiative Japan, Inc. The name of the
* IIJ may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id: modem.c,v 1.23 1996/03/29 15:24:04 ache Exp $
*
* TODO:
*/
#include "fsm.h"
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <errno.h>
#include <time.h>
#include "hdlc.h"
#include "lcp.h"
#include "ip.h"
#include "modem.h"
#include "vars.h"
#ifndef O_NONBLOCK
#ifdef O_NDELAY
#define O_NONBLOCK O_NDELAY
#endif
#endif
#define USE_CTSRTS
extern int DoChat();
static int mbits; /* Current DCD status */
static int connect_time; /* connection time */
static int connect_count;
static struct pppTimer ModemTimer;
static char uucplock[10];
extern int uu_lock(), uu_unlock();
extern void PacketMode(), TtyTermMode(), TtyCommandMode();
extern int TermMode;
#define Online (mbits & TIOCM_CD)
static struct mbuf *modemout;
static struct mqueue OutputQueues[PRI_LINK+1];
static int dev_is_modem;
#undef QDEBUG
void
Enqueue(queue, bp)
struct mqueue *queue;
struct mbuf *bp;
{
if (queue->last) {
queue->last->pnext = bp;
queue->last = bp;
} else
queue->last = queue->top = bp;
queue->qlen++;
#ifdef QDEBUG
logprintf("Enqueue: len = %d\n", queue->qlen);
#endif
}
struct mbuf *
Dequeue(queue)
struct mqueue *queue;
{
struct mbuf *bp;
#ifdef QDEBUG
logprintf("Dequeue: len = %d\n", queue->qlen);
#endif
bp = queue->top;
if (bp) {
queue->top = queue->top->pnext;
queue->qlen--;
if (queue->top == NULL) {
queue->last = queue->top;
#ifdef QDEBUG
if (queue->qlen)
logprintf("!!! not zero (%d)!!!\n", queue->qlen);
#endif
}
}
return(bp);
}
static struct speeds{
int nspeed;
speed_t speed;
} speeds[] = {
#ifdef B50
{ 50, B50, },
#endif
#ifdef B75
{ 75, B75, },
#endif
#ifdef B110
{ 110, B110, },
#endif
#ifdef B134
{ 134, B134, },
#endif
#ifdef B150
{ 150, B150, },
#endif
#ifdef B200
{ 200, B200, },
#endif
#ifdef B300
{ 300, B300, },
#endif
#ifdef B600
{ 600, B600, },
#endif
#ifdef B1200
{ 1200, B1200, },
#endif
#ifdef B1800
{ 1800, B1800, },
#endif
#ifdef B2400
{ 2400, B2400, },
#endif
#ifdef B4800
{ 4800, B4800, },
#endif
#ifdef B9600
{ 9600, B9600, },
#endif
#ifdef B19200
{ 19200, B19200, },
#endif
#ifdef B38400
{ 38400, B38400, },
#endif
#ifndef _POSIX_SOURCE
#ifdef B7200
{ 7200, B7200, },
#endif
#ifdef B14400
{ 14400, B14400, },
#endif
#ifdef B28800
{ 28800, B28800, },
#endif
#ifdef B57600
{ 57600, B57600, },
#endif
#ifdef B76800
{ 76800, B76800, },
#endif
#ifdef B115200
{ 115200, B115200, },
#endif
#ifdef B230400
{ 230400, B230400, },
#endif
#ifdef EXTA
{ 19200, EXTA, },
#endif
#ifdef EXTB
{ 38400, EXTB, },
#endif
#endif /*_POSIX_SOURCE */
{ 0, 0 }
};
int SpeedToInt(speed)
speed_t speed;
{
struct speeds *sp;
for (sp = speeds; sp->nspeed; sp++) {
if (sp->speed == speed) {
return(sp->nspeed);
}
}
return 0;
}
speed_t IntToSpeed(nspeed)
int nspeed;
{
struct speeds *sp;
for (sp = speeds; sp->nspeed; sp++) {
if (sp->nspeed == nspeed) {
return(sp->speed);
}
}
return B0;
}
static time_t uptime;
void
DownConnection()
{
LogPrintf(LOG_PHASE_BIT, "Disconnected!\n");
LogPrintf(LOG_PHASE_BIT, "Connect time: %d secs\n", time(NULL) - uptime);
if (!TermMode) {
CloseModem();
LcpDown();
}
connect_time = 0;
}
/*
* ModemTimeout() watches DCD signal and notifies if it's status is changed.
*
*/
void
ModemTimeout()
{
int ombits = mbits;
int change;
StopTimer(&ModemTimer);
if (Online)
connect_time++;
StartTimer(&ModemTimer);
if (dev_is_modem) {
ioctl(modem, TIOCMGET, &mbits);
change = ombits ^ mbits;
if (change & TIOCM_CD) {
if (Online) {
time(&uptime);
LogPrintf(LOG_PHASE_BIT, "*Connected!\n");
connect_count++;
/*
* In dedicated mode, start packet mode immediate
* after we detected carrier.
*/
if (mode & MODE_DEDICATED)
PacketMode();
} else {
DownConnection();
}
}
} else {
if (!Online) {
time(&uptime);
LogPrintf(LOG_PHASE_BIT, "Connected!\n");
mbits = TIOCM_CD;
connect_count++;
connect_time = 0;
} else if (uptime == 0) {
time(&uptime);
}
}
}
void
StartModemTimer()
{
connect_time = 0;
StopTimer(&ModemTimer);
ModemTimer.state = TIMER_STOPPED;
ModemTimer.load = SECTICKS;
ModemTimer.func = ModemTimeout;
StartTimer(&ModemTimer);
}
struct parity {
char *name;
char *name1;
int set;
} validparity[] = {
{ "even", "P_EVEN", CS7 | PARENB }, { "odd", "P_ODD", CS7 | PARENB | PARODD },
{ "none", "P_ZERO", CS8 }, { NULL, 0 },
};
int
GetParityValue(str)
char *str;
{
struct parity *pp;
for (pp = validparity; pp->name; pp++) {
if (strcasecmp(pp->name, str) == 0 ||
strcasecmp(pp->name1, str) == 0) {
VarParity = pp->set;
return(pp->set);
}
}
return(-1);
}
int
ChangeParity(str)
char *str;
{
struct termios rstio;
int val;
val = GetParityValue(str);
if (val > 0) {
VarParity = val;
tcgetattr(modem, &rstio);
rstio.c_cflag &= ~(CSIZE|PARODD|PARENB);
rstio.c_cflag |= val;
tcsetattr(modem, TCSADRAIN, &rstio);
}
return(val);
}
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int
OpenConnection(host, port)
char *host, *port;
{
struct sockaddr_in dest;
int sock;
struct hostent *hp;
struct servent *sp;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(host);
if (dest.sin_addr.s_addr == INADDR_NONE) {
hp = gethostbyname(host);
if (hp) {
bcopy(hp->h_addr_list[0], &dest.sin_addr.s_addr, 4);
} else {
printf("unknown host: %s\n", host);
return(-1);
}
}
dest.sin_port = htons(atoi(port));
if (dest.sin_port == 0) {
sp = getservbyname(port, "tcp");
if (sp) {
dest.sin_port = sp->s_port;
} else {
printf("unknown service: %s\n", port);
return(-1);
}
}
LogPrintf(LOG_PHASE_BIT, "Connected to %s:%s\n", host, port);
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return(sock);
}
if (connect(sock, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
printf("connection failed.\n");
return(-1);
}
return(sock);
}
static struct termios modemios;
int
OpenModem(mode)
int mode;
{
struct termios rstio;
int oldflag;
char *host, *cp, *port;
mbits = 0;
if (mode & MODE_DIRECT) {
if (isatty(0))
modem = open(ctermid(NULL), O_RDWR|O_NONBLOCK);
} else if (modem == 0) {
if (strncmp(VarDevice, "/dev", 4) == 0) {
strcpy(uucplock, rindex(VarDevice, '/')+1);
if (uu_lock(uucplock) < 0) {
LogPrintf(LOG_PHASE_BIT, "Modem %s is in use\n", VarDevice);
return(-1);
}
modem = open(VarDevice, O_RDWR|O_NONBLOCK);
if (modem < 0) {
LogPrintf(LOG_PHASE_BIT, "Open Failed %s\n", VarDevice);
(void) uu_unlock(uucplock);
return(modem);
}
} else {
/* XXX: PPP over TCP */
cp = index(VarDevice, ':');
if (cp) {
*cp = 0;
host = VarDevice;
port = cp + 1;
if (*host && *port) {
modem = OpenConnection(host, port);
*cp = ':'; /* Don't destroy VarDevice */
if (modem < 0) return(-1);
} else {
*cp = ':'; /* Don't destroy VarDevice */
return(-1);
}
} else
return(-1);
}
}
/* This code gets around the problem of closing descriptor 0
* when it should not have been closed and closing descriptor 1
* when the telnet connection dies. Since this program always
* opens a descriptor for the modem in auto and direct mode,
* having to dup the descriptor here is a fatal error.
*
* With the other changes I have made this should no longer happen.
* JC
*/
while (modem < 3)
{
logprintf("Duping modem fd %d\n", modem);
modem = dup(modem);
}
/*
* If we are working on tty device, change it's mode into
* the one desired for further operation. In this implementation,
* we assume that modem is configuted to use CTS/RTS flow control.
*/
dev_is_modem = isatty(modem) || DEV_IS_SYNC;
if (DEV_IS_SYNC)
sleep(1);
if (dev_is_modem && !DEV_IS_SYNC) {
tcgetattr(modem, &rstio);
modemios = rstio;
#ifdef DEBUG
logprintf("## modem = %d\n", modem);
logprintf("modem (get): iflag = %x, oflag = %x, cflag = %x\n",
rstio.c_iflag, rstio.c_oflag, rstio.c_cflag);
#endif
cfmakeraw(&rstio);
#ifdef USE_CTSRTS
rstio.c_cflag |= CLOCAL | CCTS_OFLOW|CRTS_IFLOW;
#else
rstio.c_cflag |= CLOCAL;
rstio.c_iflag |= IXOFF;
#endif
rstio.c_iflag |= IXON;
if (!(mode & MODE_DEDICATED))
rstio.c_cflag |= HUPCL;
if ((mode & MODE_DIRECT) == 0) {
/*
* If we are working as direct mode, don't change tty speed.
*/
rstio.c_cflag &= ~(CSIZE|PARODD|PARENB);
rstio.c_cflag |= VarParity;
cfsetspeed(&rstio, IntToSpeed(VarSpeed));
}
tcsetattr(modem, TCSADRAIN, &rstio);
#ifdef DEBUG
logprintf("modem (put): iflag = %x, oflag = %x, cflag = %x\n",
rstio.c_iflag, rstio.c_oflag, rstio.c_cflag);
#endif
if (!(mode & MODE_DIRECT))
ioctl(modem, TIOCMGET, &mbits);
#ifdef DEBUG
fprintf(stderr, "modem control = %o\n", mbits);
#endif
oldflag = fcntl(modem, F_GETFL, 0);
fcntl(modem, F_SETFL, oldflag & ~O_NONBLOCK);
}
StartModemTimer();
return(modem);
}
int
ModemSpeed()
{
struct termios rstio;
tcgetattr(modem, &rstio);
return(SpeedToInt(cfgetispeed(&rstio)));
}
/*
* Put modem tty line into raw mode which is necessary in packet mode operation
*/
int
RawModem(modem)
int modem;
{
struct termios rstio;
int oldflag;
if (!isatty(modem) || DEV_IS_SYNC)
return(0);
if (!(mode & MODE_DIRECT) && modem && !Online) {
#ifdef DEBUG
logprintf("mode = %d, modem = %d, mbits = %x\n", mode, modem, mbits);
#endif
#ifdef notdef
return(-1);
#endif
}
tcgetattr(modem, &rstio);
cfmakeraw(&rstio);
#ifdef USE_CTSRTS
rstio.c_cflag |= CLOCAL | CCTS_OFLOW|CRTS_IFLOW;
#else
rstio.c_cflag |= CLOCAL;
#endif
if (!(mode & MODE_DEDICATED))
rstio.c_cflag |= HUPCL;
tcsetattr(modem, TCSADRAIN, &rstio);
oldflag = fcntl(modem, F_GETFL, 0);
fcntl(modem, F_SETFL, oldflag | O_NONBLOCK);
#ifdef DEBUG
oldflag = fcntl(modem, F_GETFL, 0);
logprintf("modem (put2): iflag = %x, oflag = %x, cflag = %x\n",
rstio.c_iflag, rstio.c_oflag, rstio.c_cflag);
logprintf("flag = %x\n", oldflag);
#endif
return(0);
}
void
UnrawModem(modem)
int modem;
{
int oldflag;
if (isatty(modem) && !DEV_IS_SYNC) {
tcsetattr(modem, TCSAFLUSH, &modemios);
oldflag = fcntl(modem, F_GETFL, 0);
fcntl(modem, F_SETFL, oldflag & ~O_NONBLOCK);
}
}
void
HangupModem(flag)
int flag;
{
struct termios tio;
if (!isatty(modem)) {
mbits &= ~TIOCM_DTR;
close(modem);
modem = 0; /* Mark as modem has closed */
return;
}
if (modem && Online) {
mbits &= ~TIOCM_DTR;
#ifdef __bsdi__ /* not a POSIX way */
ioctl(modem, TIOCMSET, &mbits);
#else
tcgetattr(modem, &tio);
cfsetspeed(&tio, B0);
tcsetattr(modem, TCSANOW, &tio);
#endif
sleep(1);
#ifdef notdef
mbits &= ~TIOCM_CD;
#endif
}
/*
* If we are working as dedicated mode, never close it
* until we are directed to quit program.
*/
if (modem && (flag || !(mode & MODE_DEDICATED))) {
ModemTimeout(); /* XXX */
StopTimer(&ModemTimer); /* XXX */
/* ModemTimeout() may call DownConection() to close the modem
* resulting in modem == 0.
*/
if (modem)
{
tcflush(modem, TCIOFLUSH);
UnrawModem(modem);
close(modem);
}
(void) uu_unlock(uucplock);
modem = 0; /* Mark as modem has closed */
} else if (modem) {
mbits |= TIOCM_DTR;
#ifndef notyet
ioctl(modem, TIOCMSET, &mbits);
#else
tcgetattr(modem, &ts);
cfsetspeed(&ts, IntToSpeed(VarSpeed));
tcsetattr(modem, TCSADRAIN, &ts);
#endif
}
}
void
CloseModem()
{
if (modem >= 3)
{
close(modem);
modem = 0;
}
(void) uu_unlock(uucplock);
}
/*
* Write to modem. Actualy, requested packets are queued, and goes out
* to the line when ModemStartOutput() is called.
*/
void
WriteModem(pri, ptr, count)
int pri;
char *ptr;
int count;
{
struct mbuf *bp;
bp = mballoc(count, MB_MODEM);
bcopy(ptr, MBUF_CTOP(bp), count);
/* Should be NORMAL and LINK only.
* All IP frames get here marked NORMAL.
*/
Enqueue(&OutputQueues[pri], bp);
}
void
ModemOutput(pri, bp)
int pri;
struct mbuf *bp;
{
struct mbuf *wp;
int len;
len = plength(bp);
wp = mballoc(len, MB_MODEM);
mbread(bp, MBUF_CTOP(wp), len);
Enqueue(&OutputQueues[pri], wp);
ModemStartOutput(modem);
}
int
ModemQlen()
{
struct mqueue *queue;
int len = 0;
int i;
for ( i = PRI_NORMAL; i <= PRI_LINK; i ++ ) {
queue = &OutputQueues[i];
len += queue->qlen;
}
return(len);
}
void
ModemStartOutput(fd)
int fd;
{
struct mqueue *queue;
int nb, nw, i;
if (modemout == NULL && ModemQlen() == 0)
IpStartOutput();
if (modemout == NULL) {
i = PRI_LINK;
for (queue = &OutputQueues[PRI_LINK]; queue >= OutputQueues; queue--) {
if (queue->top) {
modemout = Dequeue(queue);
#ifdef QDEBUG
if (i > PRI_NORMAL) {
struct mqueue *q;
q = &OutputQueues[0];
logprintf("output from queue %d, normal has %d\n", i, q->qlen);
}
logprintf("Dequeue(%d): ", i);
#endif
break;
}
i--;
}
}
if (modemout) {
nb = modemout->cnt;
if (nb > 1600) nb = 1600;
if (fd == 0) fd = 1; /* XXX WTFO! This is bogus */
nw = write(fd, MBUF_CTOP(modemout), nb);
#ifdef QDEBUG
logprintf("wrote: %d(%d)\n", nw, nb);
LogDumpBuff(LOG_HDLC, "modem write", MBUF_CTOP(modemout), nb);
#endif
if (nw > 0) {
modemout->cnt -= nw;
modemout->offset += nw;
if (modemout->cnt == 0) {
modemout = mbfree(modemout);
#ifdef QDEBUG
logprintf(" mbfree\n");
#endif
}
} else if (nw < 0) {
if (errno != EAGAIN)
perror("modem write");
}
}
}
int
DialModem()
{
char ScriptBuffer[200];
strcpy(ScriptBuffer, VarDialScript);
if (DoChat(ScriptBuffer) > 0) {
if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
fprintf(stderr, "dial OK!\n");
strcpy(ScriptBuffer, VarLoginScript);
if (DoChat(ScriptBuffer) > 0) {
if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
fprintf(stderr, "login OK!\n");
return(1);
} else {
if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
fprintf(stderr, "login failed.\n");
}
ModemTimeout(); /* Dummy call to check modem status */
}
else {
if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
fprintf(stderr, "dial failed.\n");
}
HangupModem(0);
return(0);
}
int
ShowModemStatus()
{
#ifdef TIOCOUTQ
int nb;
#endif
printf("device: %s speed: ", VarDevice);
if (DEV_IS_SYNC)
printf("sync\n");
else
printf("%d\n", VarSpeed);
switch (VarParity & CSIZE) {
case CS7:
printf("cs7, ");
break;
case CS8:
printf("cs8, ");
break;
}
if (VarParity & PARENB) {
if (VarParity & PARODD)
printf("odd parity\n");
else
printf("even parity\n");
} else
printf("none parity\n");
#ifdef DEBUG
printf("fd = %d, modem control = %o\n", modem, mbits);
#endif
printf("connect count: %d\n", connect_count);
#ifdef TIOCOUTQ
ioctl(modem, TIOCOUTQ, &nb);
printf("outq: %d\n", nb);
#endif
printf("outqlen: %d\n", ModemQlen());
printf("DialScript = %s\n", VarDialScript);
printf("LoginScript = %s\n", VarLoginScript);
printf("PhoneNumber(s) = %s\n", VarPhoneList);
return(1);
}