]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/main.c
changes to linux interface for new kernel driver,
[ppp.git] / pppd / main.c
index 4de4b994156c801566415b34693dc6ee59bb0ff1..982ab55c4e4198ab401793a79f47ff72337e151e 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.87 1999/11/15 03:55:37 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.91 2000/03/27 06:03:01 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -40,6 +40,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "pppd.h"
 #include "magic.h"
@@ -92,6 +93,10 @@ int prepass = 0;             /* doing prepass to find device name */
 int devnam_fixed;              /* set while in options.ttyxx file */
 volatile int 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 */
+
 int (*holdoff_hook) __P((void)) = NULL;
 int (*new_phase_hook) __P((int)) = NULL;
 
@@ -167,6 +172,7 @@ static void holdoff_end __P((void *));
 static int device_script __P((char *, int, int, int));
 static int reap_kids __P((int waitfor));
 static void record_child __P((int, char *, void (*) (void *), void *));
+static int open_socket __P((char *));
 static int start_charshunt __P((int, int));
 static void charshunt_done __P((void *));
 static void charshunt __P((int, int, char *));
@@ -219,7 +225,7 @@ main(argc, argv)
 {
     int i, fdflags, t;
     struct sigaction sa;
-    char *p;
+    char *p, *connector;
     struct passwd *pw;
     struct timeval timo;
     sigset_t mask;
@@ -291,7 +297,7 @@ main(argc, argv)
     /*
      * Work out the device name, if it hasn't already been specified.
      */
-    using_pty = notty || ptycommand != NULL;
+    using_pty = notty || ptycommand != NULL || pty_socket != NULL;
     if (!using_pty && default_device) {
        char *p;
        if (!isatty(0) || (p = ttyname(0)) == NULL) {
@@ -306,7 +312,7 @@ main(argc, argv)
     /*
      * Parse the tty options file and the command line.
      * The per-tty options file should not change
-     * ptycommand, notty or devnam.
+     * ptycommand, pty_socket, notty or devnam.
      */
     devnam_fixed = 1;
     if (!using_pty) {
@@ -341,12 +347,12 @@ main(argc, argv)
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
        if (protp->check_options != NULL)
            (*protp->check_options)();
-    if (demand && connector == 0) {
+    if (demand && connect_script == 0) {
        option_error("connect script is required for demand-dialling\n");
        exit(EXIT_OPTION_ERROR);
     }
     /* default holdoff to 0 if no connect script has been given */
-    if (connector == 0 && !holdoff_specified)
+    if (connect_script == 0 && !holdoff_specified)
        holdoff = 0;
 
     if (using_pty) {
@@ -359,6 +365,10 @@ main(argc, argv)
            option_error("pty option is incompatible with notty option");
            exit(EXIT_OPTION_ERROR);
        }
+       if (pty_socket != NULL && (ptycommand != NULL || notty)) {
+           option_error("socket option is incompatible with pty and notty");
+           exit(EXIT_OPTION_ERROR);
+       }
        default_device = notty;
        lockflag = 0;
        modem = 0;
@@ -516,6 +526,7 @@ main(argc, argv)
        demand_conf();
     }
 
+    do_callback = 0;
     for (;;) {
 
        need_holdoff = 1;
@@ -523,8 +534,10 @@ main(argc, argv)
        real_ttyfd = -1;
        status = EXIT_OK;
        ++unsuccess;
+       doing_callback = do_callback;
+       do_callback = 0;
 
-       if (demand) {
+       if (demand && !doing_callback) {
            /*
             * Don't do anything until we see some activity.
             */
@@ -569,13 +582,13 @@ main(argc, argv)
        new_phase(PHASE_SERIALCONN);
 
        /*
-        * Get a pty master/slave pair if the pty, notty, or record
-        * options were specified.
+        * Get a pty master/slave pair if the pty, notty, socket,
+        * or record options were specified.
         */
        strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
        pty_master = -1;
        pty_slave = -1;
-       if (ptycommand != NULL || notty || record_file != NULL) {
+       if (using_pty || record_file != NULL) {
            if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
                error("Couldn't allocate pseudo-tty");
                status = EXIT_FATAL_ERROR;
@@ -603,6 +616,7 @@ main(argc, argv)
         */
        hungup = 0;
        kill_link = 0;
+       connector = doing_callback? callback_script: connect_script;
        if (devnam[0] != 0) {
            for (;;) {
                /* If the user specified the device name, become the
@@ -653,7 +667,7 @@ main(argc, argv)
        }
 
        /*
-        * If the notty and/or record option was specified,
+        * If the pty, socket, notty and/or record option was specified,
         * start up the character shunt now.
         */
        status = EXIT_PTYCMD_FAILED;
@@ -678,6 +692,12 @@ main(argc, argv)
                close(pty_master);
                pty_master = -1;
            }
+       } else if (pty_socket != NULL) {
+           int fd = open_socket(pty_socket);
+           if (fd < 0)
+               goto fail;
+           if (!start_charshunt(fd, fd))
+               goto fail;
        } else if (notty) {
            if (!start_charshunt(0, 1))
                goto fail;
@@ -689,6 +709,7 @@ main(argc, argv)
        /* 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);
@@ -724,6 +745,9 @@ main(argc, argv)
               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 */
@@ -757,7 +781,7 @@ main(argc, argv)
            goto disconnect;
        }
 
-       if (!demand) {
+       if (!demand && ifunit >= 0) {
            
            info("Using interface ppp%d", ifunit);
            slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
@@ -783,16 +807,16 @@ main(argc, argv)
         * time for something from the peer.  This can avoid bouncing
         * our packets off his tty before he has it set up.
         */
-       if (connector != NULL || ptycommand != NULL) {
+       add_fd(fd_ppp);
+       if (connect_delay != 0 && (connector != NULL || ptycommand != NULL)) {
            struct timeval t;
-           t.tv_sec = 1;
-           t.tv_usec = 0;
+           t.tv_sec = connect_delay / 1000;
+           t.tv_usec = connect_delay % 1000;
            wait_input(&t);
        }
 
        lcp_open(0);            /* Start protocol */
        open_ccp_flag = 0;
-       add_fd(fd_ppp);
        status = EXIT_NEGOTIATION_FAILED;
        new_phase(PHASE_ESTABLISH);
        while (phase != PHASE_DEAD) {
@@ -865,11 +889,11 @@ main(argc, argv)
         * XXX we may not be able to do this if the line has hung up!
         */
     disconnect:
-       if (disconnector && !hungup) {
+       if (disconnect_script && !hungup) {
            new_phase(PHASE_DISCONNECT);
            if (real_ttyfd >= 0)
                set_up_tty(real_ttyfd, 1);
-           if (device_script(disconnector, ttyfd, ttyfd, 0) < 0) {
+           if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
                warn("disconnect script failed");
            } else {
                info("Serial link disconnected.");
@@ -1052,6 +1076,90 @@ holdoff_end(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(proto)
+    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.
  */
@@ -1125,8 +1233,13 @@ get_input()
        }
     }
 
-    if (debug)
-       warn("Unsupported protocol (0x%x) received", protocol);
+    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);
 }
 
@@ -1725,9 +1838,9 @@ reap_kids(waitfor)
        return 0;
     while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1
           && pid != 0) {
-       --n_children;
        for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
            if (chp->pid == pid) {
+               --n_children;
                *prevp = chp->next;
                break;
            }
@@ -1837,6 +1950,60 @@ script_unsetenv(var)
     }
 }
 
+/*
+ * open_socket - establish a stream socket connection to the nominated
+ * host and port.
+ */
+static int
+open_socket(dest)
+    char *dest;
+{
+    char *sep, *endp = NULL;
+    int sock, port = -1;
+    u_int32_t host;
+    struct hostent *hent;
+    struct sockaddr_in sad;
+
+    /* parse host:port and resolve host to an IP address */
+    sep = strchr(dest, ':');
+    if (sep != NULL)
+       port = strtol(sep+1, &endp, 10);
+    if (port < 0 || endp == sep+1 || sep == dest) {
+       error("Can't parse host:port for socket destination");
+       return -1;
+    }
+    *sep = 0;
+    host = inet_addr(dest);
+    if (host == (u_int32_t) -1) {
+       hent = gethostbyname(dest);
+       if (hent == NULL) {
+           error("%s: unknown host in socket option", dest);
+           *sep = ':';
+           return -1;
+       }
+       host = *(u_int32_t *)(hent->h_addr_list[0]);
+    }
+    *sep = ':';
+
+    /* get a socket and connect it to the other end */
+    sock = socket(PF_INET, SOCK_STREAM, 0);
+    if (sock < 0) {
+       error("Can't create socket: %m");
+       return -1;
+    }
+    memset(&sad, 0, sizeof(sad));
+    sad.sin_family = AF_INET;
+    sad.sin_port = htons(port);
+    sad.sin_addr.s_addr = host;
+    if (connect(sock, &sad, sizeof(sad)) < 0) {
+       error("Can't connect to %s: %m", dest);
+       close(sock);
+       return -1;
+    }
+
+    return sock;
+}
+
 /*
  * start_charshunt - create a child process to run the character shunt.
  */
@@ -1897,6 +2064,8 @@ charshunt(ifd, ofd, record_file)
     int pty_readable, stdin_readable;
     struct timeval lasttime;
     FILE *recordf = NULL;
+    int ilevel, olevel, max_level;
+    struct timeval levelt, tout, *top;
 
     /*
      * Reset signal handlers.
@@ -1970,6 +2139,16 @@ charshunt(ifd, ofd, record_file)
     nibuf = nobuf = 0;
     ibufp = obufp = NULL;
     pty_readable = stdin_readable = 1;
+
+    ilevel = olevel = 0;
+    gettimeofday(&levelt, NULL);
+    if (max_data_rate) {
+       max_level = max_data_rate / 10;
+       if (max_level < 100)
+           max_level = 100;
+    } else
+       max_level = sizeof(inpacket_buf) + 1;
+
     nfds = (ofd > pty_master? ofd: pty_master) + 1;
     if (recordf != NULL) {
        gettimeofday(&lasttime, NULL);
@@ -1982,21 +2161,44 @@ charshunt(ifd, ofd, record_file)
     }
 
     while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+       top = 0;
+       tout.tv_sec = 0;
+       tout.tv_usec = 10000;
        FD_ZERO(&ready);
        FD_ZERO(&writey);
-       if (nibuf != 0)
-           FD_SET(pty_master, &writey);
-       else if (stdin_readable)
+       if (nibuf != 0) {
+           if (ilevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(pty_master, &writey);
+       } else if (stdin_readable)
            FD_SET(ifd, &ready);
-       if (nobuf != 0)
-           FD_SET(ofd, &writey);
-       else if (pty_readable)
+       if (nobuf != 0) {
+           if (olevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(ofd, &writey);
+       } else if (pty_readable)
            FD_SET(pty_master, &ready);
-       if (select(nfds, &ready, &writey, NULL, NULL) < 0) {
+       if (select(nfds, &ready, &writey, NULL, top) < 0) {
            if (errno != EINTR)
                fatal("select");
            continue;
        }
+       if (max_data_rate) {
+           double dt;
+           int nbt;
+           struct timeval now;
+
+           gettimeofday(&now, NULL);
+           dt = (now.tv_sec - levelt.tv_sec
+                 + (now.tv_usec - levelt.tv_usec) / 1e6);
+           nbt = (int)(dt * max_data_rate);
+           ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+           olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+           levelt = now;
+       } else
+           ilevel = olevel = 0;
        if (FD_ISSET(ifd, &ready)) {
            ibufp = inpacket_buf;
            nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
@@ -2052,7 +2254,10 @@ charshunt(ifd, ofd, record_file)
            }
        }
        if (FD_ISSET(ofd, &writey)) {
-           n = write(ofd, obufp, nobuf);
+           n = nobuf;
+           if (olevel + n > max_level)
+               n = max_level - olevel;
+           n = write(ofd, obufp, n);
            if (n < 0) {
                if (errno != EIO) {
                    error("Error writing standard output: %m");
@@ -2063,10 +2268,14 @@ charshunt(ifd, ofd, record_file)
            } else {
                obufp += n;
                nobuf -= n;
+               olevel += n;
            }
        }
        if (FD_ISSET(pty_master, &writey)) {
-           n = write(pty_master, ibufp, nibuf);
+           n = nibuf;
+           if (ilevel + n > max_level)
+               n = max_level - ilevel;
+           n = write(pty_master, ibufp, n);
            if (n < 0) {
                if (errno != EIO) {
                    error("Error writing pseudo-tty master: %m");
@@ -2077,6 +2286,7 @@ charshunt(ifd, ofd, record_file)
            } else {
                ibufp += n;
                nibuf -= n;
+               ilevel += n;
            }
        }
     }