mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-05-14 04:19:34 +08:00
897 lines
22 KiB
C
897 lines
22 KiB
C
/*
|
|
* main.c - Point-to-Point Protocol main module
|
|
*
|
|
* Copyright (c) 1989 Carnegie Mellon University.
|
|
* All rights reserved.
|
|
*
|
|
* 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 Carnegie Mellon University. The name of the
|
|
* University 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.
|
|
*/
|
|
|
|
#define RCSID "$Id$"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <setjmp.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/rtems_bsdnet.h>
|
|
#include <rtems/rtemsdialer.h>
|
|
|
|
#include "pppd.h"
|
|
#include "magic.h"
|
|
#include "fsm.h"
|
|
#include "lcp.h"
|
|
#include "ipcp.h"
|
|
#ifdef INET6
|
|
#include "ipv6cp.h"
|
|
#endif
|
|
#include "upap.h"
|
|
#include "chap.h"
|
|
#include "ccp.h"
|
|
#include "pathnames.h"
|
|
#include "patchlevel.h"
|
|
|
|
#ifdef CBCP_SUPPORT
|
|
#include "cbcp.h"
|
|
#endif
|
|
|
|
#ifdef IPX_CHANGE
|
|
#include "ipxcp.h"
|
|
#endif /* IPX_CHANGE */
|
|
#ifdef AT_CHANGE
|
|
#include "atcp.h"
|
|
#endif
|
|
|
|
static const char rcsid[] = RCSID;
|
|
|
|
/* interface vars */
|
|
char ifname[32]; /* Interface name */
|
|
int pppifunit; /* Interface unit number */
|
|
|
|
char hostname[MAXNAMELEN]; /* Our hostname */
|
|
static char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */
|
|
|
|
int pppd_ttyfd; /* Serial port file descriptor */
|
|
int baud_rate; /* Actual bits/second for serial device */
|
|
int hungup; /* terminal has been hung up */
|
|
int privileged; /* we're running as real uid root */
|
|
int need_holdoff; /* need holdoff period before restarting */
|
|
int detached; /* have detached from terminal */
|
|
struct stat devstat; /* result of stat() on devnam */
|
|
int prepass = 0; /* doing prepass to find device name */
|
|
int devnam_fixed; /* set while in options.ttyxx file */
|
|
volatile int pppd_status; /* exit status for pppd */
|
|
int unsuccess; /* # unsuccessful connection attempts */
|
|
int do_callback; /* != 0 if we should do callback next */
|
|
int doing_callback; /* != 0 if we are doing callback */
|
|
char *callback_script; /* script for doing callback */
|
|
dialerfp pppd_dialer;
|
|
|
|
int (*holdoff_hook)(void) = NULL;
|
|
int (*new_phase_hook)(int) = NULL;
|
|
|
|
static int fd_ppp = -1; /* fd for talking PPP */
|
|
static int pty_master; /* fd for master side of pty */
|
|
static int pty_slave; /* fd for slave side of pty */
|
|
static int real_ttyfd; /* fd for actual serial port (not pty) */
|
|
|
|
int pppd_phase; /* where the link is at */
|
|
int pppd_kill_link;
|
|
int open_ccp_flag;
|
|
|
|
char **script_env; /* Env. variable values for scripts */
|
|
int s_env_nalloc; /* # words avail at script_env */
|
|
|
|
u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
|
|
u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
|
|
|
|
char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
|
|
|
|
static struct timeval start_time; /* Time when link was started. */
|
|
|
|
struct pppd_stats link_stats;
|
|
int link_connect_time;
|
|
int link_stats_valid;
|
|
|
|
/* Prototypes for procedures local to this file. */
|
|
|
|
static void cleanup(void);
|
|
static void close_tty(void);
|
|
static void get_input(void);
|
|
static void calltimeout(void);
|
|
static struct timeval *timeleft(struct timeval *);
|
|
static void holdoff_end(void *);
|
|
static int device_script(int, int, char *);
|
|
|
|
extern char *ttyname(int);
|
|
extern char *getlogin(void);
|
|
int pppdmain(int, char *[]);
|
|
|
|
/*
|
|
* PPP Data Link Layer "protocol" table.
|
|
* One entry per supported protocol.
|
|
* The last entry must be NULL.
|
|
*/
|
|
struct protent *protocols[] = {
|
|
&lcp_protent,
|
|
&pap_protent,
|
|
&chap_protent,
|
|
#ifdef CBCP_SUPPORT
|
|
&cbcp_protent,
|
|
#endif
|
|
&ipcp_protent,
|
|
#ifdef INET6
|
|
&ipv6cp_protent,
|
|
#endif
|
|
&ccp_protent,
|
|
#ifdef IPX_CHANGE
|
|
&ipxcp_protent,
|
|
#endif
|
|
#ifdef AT_CHANGE
|
|
&atcp_protent,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
int
|
|
pppdmain(
|
|
int argc,
|
|
char *argv[])
|
|
{
|
|
int i, fdflags, t;
|
|
char *connector;
|
|
struct timeval timo;
|
|
struct protent *protp;
|
|
|
|
new_phase(PHASE_INITIALIZE);
|
|
|
|
script_env = NULL;
|
|
hostname[MAXNAMELEN-1] = 0;
|
|
privileged = 1;
|
|
privileged_option = 1;
|
|
|
|
/*
|
|
* Initialize magic number generator now so that protocols may
|
|
* use magic numbers in initialization.
|
|
*/
|
|
magic_init();
|
|
|
|
#ifdef XXX_XXX
|
|
/* moved code the the rtems_pppd_reset_options function */
|
|
|
|
/*
|
|
* Initialize to the standard option set, then parse, in order,
|
|
* the system options file, the user's options file,
|
|
* the tty's options file, and the command line arguments.
|
|
*/
|
|
for (i = 0; (protp = protocols[i]) != NULL; ++i)
|
|
(*protp->init)(0);
|
|
#endif
|
|
|
|
|
|
if (!ppp_available()) {
|
|
option_error(no_ppp_msg);
|
|
return(EXIT_NO_KERNEL_SUPPORT);
|
|
}
|
|
|
|
/*
|
|
* Check that the options given are valid and consistent.
|
|
*/
|
|
if (!sys_check_options()) {
|
|
return(EXIT_OPTION_ERROR);
|
|
}
|
|
if (!auth_check_options()) {
|
|
return(EXIT_OPTION_ERROR);
|
|
}
|
|
for (i = 0; (protp = protocols[i]) != NULL; ++i)
|
|
if (protp->check_options != NULL)
|
|
(*protp->check_options)();
|
|
|
|
/* default holdoff to 0 if no connect script has been given */
|
|
if (connect_script == 0 && !holdoff_specified)
|
|
holdoff = 0;
|
|
|
|
if (default_device)
|
|
nodetach = 1;
|
|
|
|
/*
|
|
* Initialize system-dependent stuff.
|
|
*/
|
|
sys_init();
|
|
/* if (debug)
|
|
setlogmask(LOG_UPTO(LOG_DEBUG));
|
|
*/
|
|
|
|
do_callback = 0;
|
|
for (;;) {
|
|
|
|
need_holdoff = 1;
|
|
pppd_ttyfd = -1;
|
|
real_ttyfd = -1;
|
|
pppd_status = EXIT_OK;
|
|
++unsuccess;
|
|
doing_callback = do_callback;
|
|
do_callback = 0;
|
|
|
|
new_phase(PHASE_SERIALCONN);
|
|
|
|
/*
|
|
* Get a pty master/slave pair if the pty, notty, or record
|
|
* options were specified.
|
|
*/
|
|
strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
|
|
pty_master = -1;
|
|
pty_slave = -1;
|
|
|
|
/*
|
|
* Open the serial device and set it up to be the ppp interface.
|
|
* First we open it in non-blocking mode so we can set the
|
|
* various termios flags appropriately. If we aren't dialling
|
|
* out and we want to use the modem lines, we reopen it later
|
|
* in order to wait for the carrier detect signal from the modem.
|
|
*/
|
|
hungup = 0;
|
|
pppd_kill_link = 0;
|
|
connector = doing_callback? callback_script: connect_script;
|
|
if (devnam[0] != 0) {
|
|
for (;;) {
|
|
/* If the user specified the device name, become the
|
|
user before opening it. */
|
|
int err;
|
|
pppd_ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
|
|
err = errno;
|
|
if (pppd_ttyfd >= 0) {
|
|
break;
|
|
}
|
|
errno = err;
|
|
if (err != EINTR) {
|
|
error("Failed to open %s: %m", devnam);
|
|
pppd_status = EXIT_OPEN_FAILED;
|
|
}
|
|
if (!persist || err != EINTR)
|
|
goto fail;
|
|
}
|
|
if ((fdflags = fcntl(pppd_ttyfd, F_GETFL)) == -1
|
|
|| fcntl(pppd_ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
|
|
warn("Couldn't reset non-blocking mode on device: %m");
|
|
|
|
/*
|
|
* Set line speed, flow control, etc.
|
|
* If we have a non-null connection or initializer script,
|
|
* on most systems we set CLOCAL for now so that we can talk
|
|
* to the modem before carrier comes up. But this has the
|
|
* side effect that we might miss it if CD drops before we
|
|
* get to clear CLOCAL below. On systems where we can talk
|
|
* successfully to the modem with CLOCAL clear and CD down,
|
|
* we could clear CLOCAL at this point.
|
|
*/
|
|
set_up_tty(pppd_ttyfd, ((connector != NULL && connector[0] != 0)
|
|
|| initializer != NULL));
|
|
real_ttyfd = pppd_ttyfd;
|
|
}
|
|
|
|
/* run connection script */
|
|
if ((connector && connector[0]) || initializer) {
|
|
if (real_ttyfd != -1) {
|
|
/* XXX do this if doing_callback == CALLBACK_DIALIN? */
|
|
if (!default_device && modem) {
|
|
setdtr(real_ttyfd, 0); /* in case modem is off hook */
|
|
sleep(1);
|
|
setdtr(real_ttyfd, 1);
|
|
}
|
|
}
|
|
|
|
if (initializer && initializer[0]) {
|
|
if (device_script(pppd_ttyfd, DIALER_INIT, initializer) < 0) {
|
|
error("Initializer script failed");
|
|
pppd_status = EXIT_INIT_FAILED;
|
|
goto fail;
|
|
}
|
|
if (pppd_kill_link)
|
|
goto disconnect;
|
|
|
|
info("Serial port initialized.");
|
|
}
|
|
|
|
if (connector && connector[0]) {
|
|
if (device_script(pppd_ttyfd, DIALER_CONNECT, connector) < 0) {
|
|
error("Connect script failed");
|
|
pppd_status = EXIT_CONNECT_FAILED;
|
|
goto fail;
|
|
}
|
|
if (pppd_kill_link)
|
|
goto disconnect;
|
|
|
|
info("Serial connection established.");
|
|
}
|
|
|
|
/* set line speed, flow control, etc.;
|
|
clear CLOCAL if modem option */
|
|
if (real_ttyfd != -1)
|
|
set_up_tty(real_ttyfd, 0);
|
|
|
|
if (doing_callback == CALLBACK_DIALIN)
|
|
connector = NULL;
|
|
}
|
|
|
|
/* reopen tty if necessary to wait for carrier */
|
|
if (connector == NULL && modem && devnam[0] != 0) {
|
|
for (;;) {
|
|
if ((i = open(devnam, O_RDWR)) >= 0)
|
|
break;
|
|
if (errno != EINTR) {
|
|
error("Failed to reopen %s: %m", devnam);
|
|
pppd_status = EXIT_OPEN_FAILED;
|
|
}
|
|
if (!persist || errno != EINTR || hungup || pppd_kill_link)
|
|
goto fail;
|
|
}
|
|
close(i);
|
|
}
|
|
|
|
info("Serial connection established.");
|
|
sleep(1);
|
|
|
|
/* run welcome script, if any */
|
|
if (welcomer && welcomer[0]) {
|
|
if (device_script(pppd_ttyfd, DIALER_WELCOME, welcomer) < 0)
|
|
warn("Welcome script failed");
|
|
}
|
|
|
|
/* set up the serial device as a ppp interface */
|
|
fd_ppp = establish_ppp(pppd_ttyfd);
|
|
if (fd_ppp < 0) {
|
|
pppd_status = EXIT_FATAL_ERROR;
|
|
goto disconnect;
|
|
}
|
|
|
|
if (!demand) {
|
|
info("Using interface ppp%d", pppifunit);
|
|
slprintf(ifname, sizeof(ifname), "ppp%d", pppifunit);
|
|
}
|
|
|
|
/*
|
|
* Start opening the connection and wait for
|
|
* incoming events (reply, timeout, etc.).
|
|
*/
|
|
notice("Connect: %s <--> %s", ifname, ppp_devnam);
|
|
gettimeofday(&start_time, NULL);
|
|
|
|
lcp_lowerup(0);
|
|
lcp_open(0); /* Start protocol */
|
|
|
|
open_ccp_flag = 0;
|
|
pppd_status = EXIT_NEGOTIATION_FAILED;
|
|
new_phase(PHASE_ESTABLISH);
|
|
while (pppd_phase != PHASE_DEAD) {
|
|
wait_input(timeleft(&timo));
|
|
calltimeout();
|
|
get_input();
|
|
|
|
if (pppd_kill_link) {
|
|
lcp_close(0, "User request");
|
|
pppd_kill_link = 0;
|
|
}
|
|
if (open_ccp_flag) {
|
|
if (pppd_phase == PHASE_NETWORK || pppd_phase == PHASE_RUNNING) {
|
|
ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
|
|
(*ccp_protent.open)(0);
|
|
}
|
|
open_ccp_flag = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we may want to bring the link up again, transfer
|
|
* the ppp unit back to the loopback. Set the
|
|
* real serial device back to its normal mode of operation.
|
|
*/
|
|
clean_check();
|
|
if (demand)
|
|
restore_loop();
|
|
disestablish_ppp(pppd_ttyfd);
|
|
fd_ppp = -1;
|
|
if (!hungup)
|
|
lcp_lowerdown(0);
|
|
|
|
/*
|
|
* Run disconnector script, if requested.
|
|
* XXX we may not be able to do this if the line has hung up!
|
|
*/
|
|
disconnect:
|
|
if (disconnect_script && !hungup) {
|
|
new_phase(PHASE_DISCONNECT);
|
|
if (real_ttyfd >= 0)
|
|
set_up_tty(real_ttyfd, 1);
|
|
if (device_script(pppd_ttyfd, DIALER_DISCONNECT, disconnect_script) < 0) {
|
|
warn("disconnect script failed");
|
|
} else {
|
|
info("Serial link disconnected.");
|
|
}
|
|
}
|
|
|
|
fail:
|
|
if (pty_master >= 0)
|
|
close(pty_master);
|
|
if (pty_slave >= 0)
|
|
close(pty_slave);
|
|
if (real_ttyfd >= 0)
|
|
close_tty();
|
|
|
|
if (!persist || (maxfail > 0 && unsuccess >= maxfail))
|
|
break;
|
|
|
|
pppd_kill_link = 0;
|
|
if (demand)
|
|
demand_discard();
|
|
t = need_holdoff? holdoff: 0;
|
|
if (holdoff_hook)
|
|
t = (*holdoff_hook)();
|
|
if (t > 0) {
|
|
new_phase(PHASE_HOLDOFF);
|
|
TIMEOUT(holdoff_end, NULL, t);
|
|
do {
|
|
wait_input(timeleft(&timo));
|
|
|
|
calltimeout();
|
|
if (pppd_kill_link) {
|
|
pppd_kill_link = 0;
|
|
new_phase(PHASE_DORMANT); /* allow signal to end holdoff */
|
|
}
|
|
} while (pppd_phase == PHASE_HOLDOFF);
|
|
if (!persist)
|
|
break;
|
|
}
|
|
}
|
|
|
|
die(pppd_status);
|
|
return pppd_status;
|
|
}
|
|
|
|
/*
|
|
* holdoff_end - called via a timeout when the holdoff period ends.
|
|
*/
|
|
static void
|
|
holdoff_end(
|
|
void *arg)
|
|
{
|
|
new_phase(PHASE_DORMANT);
|
|
}
|
|
|
|
/* List of protocol names, to make our messages a little more informative. */
|
|
struct protocol_list {
|
|
u_short proto;
|
|
const char *name;
|
|
} protocol_list[] = {
|
|
{ 0x21, "IP" },
|
|
{ 0x23, "OSI Network Layer" },
|
|
{ 0x25, "Xerox NS IDP" },
|
|
{ 0x27, "DECnet Phase IV" },
|
|
{ 0x29, "Appletalk" },
|
|
{ 0x2b, "Novell IPX" },
|
|
{ 0x2d, "VJ compressed TCP/IP" },
|
|
{ 0x2f, "VJ uncompressed TCP/IP" },
|
|
{ 0x31, "Bridging PDU" },
|
|
{ 0x33, "Stream Protocol ST-II" },
|
|
{ 0x35, "Banyan Vines" },
|
|
{ 0x39, "AppleTalk EDDP" },
|
|
{ 0x3b, "AppleTalk SmartBuffered" },
|
|
{ 0x3d, "Multi-Link" },
|
|
{ 0x3f, "NETBIOS Framing" },
|
|
{ 0x41, "Cisco Systems" },
|
|
{ 0x43, "Ascom Timeplex" },
|
|
{ 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
|
|
{ 0x47, "DCA Remote Lan" },
|
|
{ 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
|
|
{ 0x4b, "SNA over 802.2" },
|
|
{ 0x4d, "SNA" },
|
|
{ 0x4f, "IP6 Header Compression" },
|
|
{ 0x6f, "Stampede Bridging" },
|
|
{ 0xfb, "single-link compression" },
|
|
{ 0xfd, "1st choice compression" },
|
|
{ 0x0201, "802.1d Hello Packets" },
|
|
{ 0x0203, "IBM Source Routing BPDU" },
|
|
{ 0x0205, "DEC LANBridge100 Spanning Tree" },
|
|
{ 0x0231, "Luxcom" },
|
|
{ 0x0233, "Sigma Network Systems" },
|
|
{ 0x8021, "Internet Protocol Control Protocol" },
|
|
{ 0x8023, "OSI Network Layer Control Protocol" },
|
|
{ 0x8025, "Xerox NS IDP Control Protocol" },
|
|
{ 0x8027, "DECnet Phase IV Control Protocol" },
|
|
{ 0x8029, "Appletalk Control Protocol" },
|
|
{ 0x802b, "Novell IPX Control Protocol" },
|
|
{ 0x8031, "Bridging NCP" },
|
|
{ 0x8033, "Stream Protocol Control Protocol" },
|
|
{ 0x8035, "Banyan Vines Control Protocol" },
|
|
{ 0x803d, "Multi-Link Control Protocol" },
|
|
{ 0x803f, "NETBIOS Framing Control Protocol" },
|
|
{ 0x8041, "Cisco Systems Control Protocol" },
|
|
{ 0x8043, "Ascom Timeplex" },
|
|
{ 0x8045, "Fujitsu LBLB Control Protocol" },
|
|
{ 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
|
|
{ 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
|
|
{ 0x804b, "SNA over 802.2 Control Protocol" },
|
|
{ 0x804d, "SNA Control Protocol" },
|
|
{ 0x804f, "IP6 Header Compression Control Protocol" },
|
|
{ 0x006f, "Stampede Bridging Control Protocol" },
|
|
{ 0x80fb, "Single Link Compression Control Protocol" },
|
|
{ 0x80fd, "Compression Control Protocol" },
|
|
{ 0xc021, "Link Control Protocol" },
|
|
{ 0xc023, "Password Authentication Protocol" },
|
|
{ 0xc025, "Link Quality Report" },
|
|
{ 0xc027, "Shiva Password Authentication Protocol" },
|
|
{ 0xc029, "CallBack Control Protocol (CBCP)" },
|
|
{ 0xc081, "Container Control Protocol" },
|
|
{ 0xc223, "Challenge Handshake Authentication Protocol" },
|
|
{ 0xc281, "Proprietary Authentication Protocol" },
|
|
{ 0, NULL },
|
|
};
|
|
|
|
/*
|
|
* protocol_name - find a name for a PPP protocol.
|
|
*/
|
|
const char *
|
|
protocol_name(
|
|
int proto)
|
|
{
|
|
struct protocol_list *lp;
|
|
|
|
for (lp = protocol_list; lp->proto != 0; ++lp)
|
|
if (proto == lp->proto)
|
|
return lp->name;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* get_input - called when incoming data is available.
|
|
*/
|
|
static void
|
|
get_input(void)
|
|
{
|
|
int len, i;
|
|
u_char *p;
|
|
u_short protocol;
|
|
struct protent *protp;
|
|
|
|
p = inpacket_buf; /* point to beginning of packet buffer */
|
|
|
|
len = read_packet(inpacket_buf);
|
|
if (len < 0)
|
|
return;
|
|
|
|
if (len == 0) {
|
|
notice("Modem hangup");
|
|
hungup = 1;
|
|
pppd_status = EXIT_HANGUP;
|
|
lcp_lowerdown(0); /* serial link is no longer available */
|
|
link_terminated(0);
|
|
return;
|
|
}
|
|
|
|
if (debug /*&& (debugflags & DBG_INPACKET)*/)
|
|
dbglog("rcvd %P", p, len);
|
|
|
|
if (len < PPP_HDRLEN) {
|
|
MAINDEBUG(("io(): Received short packet."));
|
|
return;
|
|
}
|
|
|
|
p += 2; /* Skip address and control */
|
|
GETSHORT(protocol, p);
|
|
len -= PPP_HDRLEN;
|
|
|
|
/*
|
|
* Toss all non-LCP packets unless LCP is OPEN.
|
|
*/
|
|
if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
|
|
MAINDEBUG(("get_input: Received non-LCP packet when LCP not open."));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Until we get past the authentication phase, toss all packets
|
|
* except LCP, LQR and authentication packets.
|
|
*/
|
|
if (pppd_phase <= PHASE_AUTHENTICATE
|
|
&& !(protocol == PPP_LCP || protocol == PPP_LQR
|
|
|| protocol == PPP_PAP || protocol == PPP_CHAP)) {
|
|
MAINDEBUG(("get_input: discarding proto 0x%x in phase %d",
|
|
protocol, pppd_phase));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Upcall the proper protocol input routine.
|
|
*/
|
|
for (i = 0; (protp = protocols[i]) != NULL; ++i) {
|
|
if (protp->protocol == protocol && protp->enabled_flag) {
|
|
(*protp->input)(0, p, len);
|
|
return;
|
|
}
|
|
if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
|
|
&& protp->datainput != NULL) {
|
|
(*protp->datainput)(0, p, len);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (debug) {
|
|
const char *pname = protocol_name(protocol);
|
|
if (pname != NULL)
|
|
warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
|
|
else
|
|
warn("Unsupported protocol 0x%x received", protocol);
|
|
}
|
|
lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* new_phase - signal the start of a new phase of pppd's operation.
|
|
*/
|
|
void
|
|
new_phase(
|
|
int p)
|
|
{
|
|
pppd_phase = p;
|
|
if (new_phase_hook)
|
|
(*new_phase_hook)(p);
|
|
}
|
|
|
|
/*
|
|
* die - clean up state and exit with the specified status.
|
|
*/
|
|
void
|
|
die(
|
|
int status)
|
|
{
|
|
cleanup();
|
|
}
|
|
|
|
/*
|
|
* cleanup - restore anything which needs to be restored before we exit
|
|
*/
|
|
/* ARGSUSED */
|
|
static void
|
|
cleanup(void)
|
|
{
|
|
sys_cleanup();
|
|
|
|
if (fd_ppp >= 0)
|
|
disestablish_ppp(pppd_ttyfd);
|
|
if (real_ttyfd >= 0)
|
|
close_tty();
|
|
|
|
sys_close();
|
|
}
|
|
|
|
/*
|
|
* close_tty - restore the terminal device and close it.
|
|
*/
|
|
static void
|
|
close_tty(void)
|
|
{
|
|
/* drop dtr to hang up */
|
|
if (!default_device && modem) {
|
|
setdtr(real_ttyfd, 0);
|
|
/*
|
|
* This sleep is in case the serial port has CLOCAL set by default,
|
|
* and consequently will reassert DTR when we close the device.
|
|
*/
|
|
sleep(1);
|
|
}
|
|
|
|
restore_tty(real_ttyfd);
|
|
|
|
close(real_ttyfd);
|
|
real_ttyfd = -1;
|
|
}
|
|
|
|
/*
|
|
* update_link_stats - get stats at link termination.
|
|
*/
|
|
void
|
|
update_link_stats(
|
|
int u)
|
|
{
|
|
struct timeval now;
|
|
char numbuf[32];
|
|
|
|
if (!get_ppp_stats(u, &link_stats)
|
|
|| gettimeofday(&now, NULL) < 0)
|
|
return;
|
|
link_connect_time = now.tv_sec - start_time.tv_sec;
|
|
link_stats_valid = 1;
|
|
|
|
slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
|
|
slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
|
|
slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
|
|
}
|
|
|
|
struct callout {
|
|
struct timeval c_time; /* time at which to call routine */
|
|
void *c_arg; /* argument to routine */
|
|
void (*c_func)(void *); /* routine */
|
|
struct callout *c_next;
|
|
};
|
|
|
|
static struct callout *callout = NULL; /* Callout list */
|
|
static struct timeval timenow; /* Current time */
|
|
|
|
/*
|
|
* timeout - Schedule a timeout.
|
|
*
|
|
* Note that this timeout takes the number of seconds, NOT hz (as in
|
|
* the kernel).
|
|
*/
|
|
void
|
|
ppptimeout(
|
|
void (*func)(void *),
|
|
void *arg,
|
|
int time)
|
|
{
|
|
struct callout *newp, *p, **pp;
|
|
|
|
MAINDEBUG(("Timeout %p:%p in %d seconds.", func, arg, time));
|
|
|
|
/*
|
|
* Allocate timeout.
|
|
*/
|
|
if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL)
|
|
fatal("Out of memory in timeout()!");
|
|
newp->c_arg = arg;
|
|
newp->c_func = func;
|
|
gettimeofday(&timenow, NULL);
|
|
newp->c_time.tv_sec = timenow.tv_sec + time;
|
|
newp->c_time.tv_usec = timenow.tv_usec;
|
|
|
|
/*
|
|
* Find correct place and link it in.
|
|
*/
|
|
for (pp = &callout; (p = *pp); pp = &p->c_next)
|
|
if (newp->c_time.tv_sec < p->c_time.tv_sec
|
|
|| (newp->c_time.tv_sec == p->c_time.tv_sec
|
|
&& newp->c_time.tv_usec < p->c_time.tv_usec))
|
|
break;
|
|
newp->c_next = p;
|
|
*pp = newp;
|
|
}
|
|
|
|
|
|
/*
|
|
* untimeout - Unschedule a timeout.
|
|
*/
|
|
void
|
|
pppuntimeout(
|
|
void (*func)(void *),
|
|
void *arg)
|
|
{
|
|
struct callout **copp, *freep;
|
|
|
|
MAINDEBUG(("Untimeout %p:%p.", func, arg));
|
|
|
|
/*
|
|
* Find first matching timeout and remove it from the list.
|
|
*/
|
|
for (copp = &callout; (freep = *copp); copp = &freep->c_next)
|
|
if (freep->c_func == func && freep->c_arg == arg) {
|
|
*copp = freep->c_next;
|
|
free((char *) freep);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* calltimeout - Call any timeout routines which are now due.
|
|
*/
|
|
static void
|
|
calltimeout(void)
|
|
{
|
|
struct callout *p;
|
|
|
|
while (callout != NULL) {
|
|
p = callout;
|
|
|
|
if (gettimeofday(&timenow, NULL) < 0)
|
|
fatal("Failed to get time of day: %m");
|
|
if (!(p->c_time.tv_sec < timenow.tv_sec
|
|
|| (p->c_time.tv_sec == timenow.tv_sec
|
|
&& p->c_time.tv_usec <= timenow.tv_usec)))
|
|
break; /* no, it's not time yet */
|
|
|
|
callout = p->c_next;
|
|
(*p->c_func)(p->c_arg);
|
|
|
|
free((char *) p);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* timeleft - return the length of time until the next timeout is due.
|
|
*/
|
|
static struct timeval *
|
|
timeleft(
|
|
struct timeval *tvp)
|
|
{
|
|
if (callout == NULL)
|
|
return NULL;
|
|
|
|
gettimeofday(&timenow, NULL);
|
|
tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
|
|
tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
|
|
if (tvp->tv_usec < 0) {
|
|
tvp->tv_usec += 1000000;
|
|
tvp->tv_sec -= 1;
|
|
}
|
|
if (tvp->tv_sec < 0)
|
|
tvp->tv_sec = tvp->tv_usec = 0;
|
|
|
|
return tvp;
|
|
}
|
|
|
|
/*
|
|
* device_script - run a program to talk to the serial device
|
|
* (e.g. to run the connector or disconnector script).
|
|
*/
|
|
static int device_script(int fd, int mode, char *program)
|
|
{
|
|
int iReturn = -1;
|
|
char pScript[128];
|
|
|
|
/* copyt script into temporary location */
|
|
strcpy(pScript, program);
|
|
|
|
/* check to see if dialer was initialized */
|
|
if ( !pppd_dialer ) {
|
|
/* set default dialer to chatmain */
|
|
pppd_dialer = chatmain;
|
|
}
|
|
|
|
/* check to see if dialer is set */
|
|
if ( pppd_dialer ) {
|
|
/* call the dialer */
|
|
iReturn = (*pppd_dialer)(fd, mode, program);
|
|
}
|
|
|
|
return ( -iReturn );
|
|
}
|
|
|
|
/*
|
|
* novm - log an error message saying we ran out of memory, and die.
|
|
*/
|
|
void
|
|
novm(
|
|
char *msg)
|
|
{
|
|
fatal("Virtual memory exhausted allocating %s\n", msg);
|
|
}
|