Multilink improvements. This involved moving some logic from the
authorPaul Mackerras <paulus@samba.org>
Fri, 12 Nov 2004 10:30:51 +0000 (10:30 +0000)
committerPaul Mackerras <paulus@samba.org>
Fri, 12 Nov 2004 10:30:51 +0000 (10:30 +0000)
main loop in main.c into link_required() and link_terminated() in
auth.c and adding code to multilink.c.  We now make a tdb entry
with the list of pppd pids for all the links in the bundle, and the
master pppd uses this to send a SIGHUP to each one when the bundle
is terminated.

We still have one pppd controlling both the bundle and the first link,
but when that link goes down, assuming that other links still exist,
the first link's pppd will clean up after that link but then stay
running until all the links have disconnected.  So it is possible to
lose the first link without losing the bundle.

This requires a small kernel patch which I will be sending to the
kernel maintainers shortly.

pppd/auth.c
pppd/lcp.c
pppd/main.c
pppd/multilink.c
pppd/pppd.h
pppd/sys-linux.c

index b9115b943757ea13250701a35bdd57fbff8c1c84..8a8b2fb83d44a4a74c6b0c5a2778291687340c8a 100644 (file)
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: auth.c,v 1.100 2004/11/06 05:39:23 paulus Exp $"
+#define RCSID  "$Id: auth.c,v 1.101 2004/11/12 10:30:51 paulus Exp $"
 
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <errno.h>
 #include <pwd.h>
 #include <grp.h>
 #include <string.h>
@@ -531,6 +532,55 @@ void
 link_required(unit)
     int unit;
 {
+    new_phase(PHASE_SERIALCONN);
+
+    devfd = the_channel->connect();
+    if (devfd < 0)
+       goto fail;
+
+    /* set up the serial device as a ppp interface */
+    /*
+     * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+     * (from establish_ppp to set_ifunit).  However, we won't be
+     * doing the set_ifunit in multilink mode, which is the only time
+     * we need the atomicity that the tdb_writelock/tdb_writeunlock
+     * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.
+     */
+    fd_ppp = the_channel->establish_ppp(devfd);
+    if (fd_ppp < 0) {
+       status = EXIT_FATAL_ERROR;
+       goto disconnect;
+    }
+
+    if (!demand && ifunit >= 0)
+       set_ifunit(1);
+
+    /*
+     * Start opening the connection and wait for
+     * incoming events (reply, timeout, etc.).
+     */
+    if (ifunit >= 0)
+       notice("Connect: %s <--> %s", ifname, ppp_devnam);
+    else
+       notice("Starting negotiation on %s", ppp_devnam);
+    add_fd(fd_ppp);
+
+    status = EXIT_NEGOTIATION_FAILED;
+    new_phase(PHASE_ESTABLISH);
+
+    lcp_lowerup(0);
+    return;
+
+ disconnect:
+    new_phase(PHASE_DISCONNECT);
+    if (the_channel->disconnect)
+       the_channel->disconnect();
+
+ fail:
+    new_phase(PHASE_DEAD);
+    if (the_channel->cleanup)
+       (*the_channel->cleanup)();
+
 }
 
 /*
@@ -541,16 +591,65 @@ void
 link_terminated(unit)
     int unit;
 {
-    if (phase == PHASE_DEAD)
+    if (phase == PHASE_DEAD || phase == PHASE_MASTER)
        return;
+    new_phase(PHASE_DISCONNECT);
+
     if (pap_logout_hook) {
        pap_logout_hook();
     } else {
        if (logged_in)
            plogout();
     }
-    new_phase(PHASE_DEAD);
-    notice("Connection terminated.");
+
+    if (!doing_multilink) {
+       notice("Connection terminated.");
+       print_link_stats();
+    } else
+       notice("Link terminated.");
+
+    /*
+     * Delete pid files before disestablishing ppp.  Otherwise it
+     * can happen that another pppd gets the same unit and then
+     * we delete its pid file.
+     */
+    if (!doing_multilink && !demand)
+       remove_pidfiles();
+
+    /*
+     * 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.
+     */
+    if (fd_ppp >= 0) {
+       remove_fd(fd_ppp);
+       clean_check();
+       the_channel->disestablish_ppp(devfd);
+       if (doing_multilink)
+           mp_exit_bundle();
+       fd_ppp = -1;
+    }
+    if (!hungup)
+       lcp_lowerdown(0);
+    if (!doing_multilink && !demand)
+       script_unsetenv("IFNAME");
+
+    /*
+     * Run disconnector script, if requested.
+     * XXX we may not be able to do this if the line has hung up!
+     */
+    if (devfd >= 0 && the_channel->disconnect) {
+       the_channel->disconnect();
+       devfd = -1;
+    }
+
+    if (doing_multilink && multilink_master) {
+       if (!bundle_terminating)
+           new_phase(PHASE_MASTER);
+       else
+           mp_bundle_terminated();
+    } else
+       new_phase(PHASE_DEAD);
 }
 
 /*
@@ -559,17 +658,30 @@ link_terminated(unit)
 void
 link_down(unit)
     int unit;
+{
+    if (auth_state != s_down) {
+       notify(link_down_notifier, 0);
+       auth_state = s_down;
+       if (auth_script_state == s_up && auth_script_pid == 0) {
+           update_link_stats(unit);
+           auth_script_state = s_down;
+           auth_script(_PATH_AUTHDOWN);
+       }
+    }
+    if (!doing_multilink) {
+       upper_layers_down(unit);
+       if (phase != PHASE_DEAD && phase != PHASE_MASTER)
+           new_phase(PHASE_ESTABLISH);
+    }
+    /* XXX if doing_multilink, should do something to stop
+       network-layer traffic on the link */
+}
+
+void upper_layers_down(int unit)
 {
     int i;
     struct protent *protp;
 
-    notify(link_down_notifier, 0);
-    auth_state = s_down;
-    if (auth_script_state == s_up && auth_script_pid == 0) {
-       update_link_stats(unit);
-       auth_script_state = s_down;
-       auth_script(_PATH_AUTHDOWN);
-    }
     for (i = 0; (protp = protocols[i]) != NULL; ++i) {
        if (!protp->enabled_flag)
            continue;
@@ -580,8 +692,6 @@ link_down(unit)
     }
     num_np_open = 0;
     num_np_up = 0;
-    if (phase != PHASE_DEAD)
-       new_phase(PHASE_ESTABLISH);
 }
 
 /*
@@ -602,10 +712,12 @@ link_established(unit)
     /*
      * Tell higher-level protocols that LCP is up.
      */
-    for (i = 0; (protp = protocols[i]) != NULL; ++i)
-        if (protp->protocol != PPP_LCP && protp->enabled_flag
-           && protp->lowerup != NULL)
-           (*protp->lowerup)(unit);
+    if (!doing_multilink) {
+       for (i = 0; (protp = protocols[i]) != NULL; ++i)
+           if (protp->protocol != PPP_LCP && protp->enabled_flag
+               && protp->lowerup != NULL)
+               (*protp->lowerup)(unit);
+    }
 
     if (!auth_required && noauth_addrs != NULL)
        set_allowed_addrs(unit, NULL, NULL);
index dde1381abae4c95ca89d662f36da3b002950f913..a06300aeb73e3c21739ca824c6490776b8a89305 100644 (file)
@@ -40,7 +40,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: lcp.c,v 1.71 2004/10/31 22:23:18 paulus Exp $"
+#define RCSID  "$Id: lcp.c,v 1.72 2004/11/12 10:30:51 paulus Exp $"
 
 /*
  * TODO:
@@ -401,7 +401,7 @@ lcp_close(unit, reason)
 {
     fsm *f = &lcp_fsm[unit];
 
-    if (phase != PHASE_DEAD)
+    if (phase != PHASE_DEAD && phase != PHASE_MASTER)
        new_phase(PHASE_TERMINATE);
     if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
        /*
index 4a91e06e4b846793abe63845260314901224c4ee..285f219061f3a2ff53d269916b4400cdeb4d1a07 100644 (file)
@@ -66,7 +66,7 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: main.c,v 1.144 2004/11/09 22:35:02 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.145 2004/11/12 10:30:51 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -165,11 +165,11 @@ void (*snoop_recv_hook) __P((unsigned char *p, int len)) = NULL;
 void (*snoop_send_hook) __P((unsigned char *p, int len)) = NULL;
 
 static int conn_running;       /* we have a [dis]connector running */
-static int devfd;              /* fd of underlying device */
-static int fd_ppp = -1;                /* fd for talking PPP */
 static int fd_loop;            /* fd for getting demand-dial packets */
-static int fd_devnull;         /* fd for /dev/null */
 
+int fd_devnull;                        /* fd for /dev/null */
+int devfd = -1;                        /* fd of underlying device */
+int fd_ppp = -1;               /* fd for talking PPP */
 int phase;                     /* where the link is at */
 int kill_link;
 int open_ccp_flag;
@@ -207,6 +207,9 @@ int link_stats_valid;
 
 int error_count;
 
+bool bundle_eof;
+bool bundle_terminating;
+
 /*
  * We maintain a list of child process pids and
  * functions to call when they exit.
@@ -488,12 +491,13 @@ main(argc, argv)
         * Configure the interface and mark it up, etc.
         */
        demand_conf();
-       create_linkpidfile(getpid());
     }
 
     do_callback = 0;
     for (;;) {
 
+       bundle_eof = 0;
+       bundle_terminating = 0;
        listen_time = 0;
        need_holdoff = 1;
        devfd = -1;
@@ -527,57 +531,21 @@ main(argc, argv)
            info("Starting link");
        }
 
-       new_phase(PHASE_SERIALCONN);
-
-       devfd = the_channel->connect();
-       if (devfd < 0)
-           goto fail;
-
-       /* set up the serial device as a ppp interface */
-#ifdef USE_TDB
-       tdb_writelock(pppdb);
-#endif
-       fd_ppp = the_channel->establish_ppp(devfd);
-       if (fd_ppp < 0) {
-#ifdef USE_TDB
-           tdb_writeunlock(pppdb);
-#endif
-           status = EXIT_FATAL_ERROR;
-           goto disconnect;
-       }
-       /* create the pid file, now that we've obtained a ppp interface */
-       if (!demand)
-           create_linkpidfile(getpid());
-
-       if (!demand && ifunit >= 0)
-           set_ifunit(1);
-#ifdef USE_TDB
-       tdb_writeunlock(pppdb);
-#endif
-
-       /*
-        * Start opening the connection and wait for
-        * incoming events (reply, timeout, etc.).
-        */
-       if (ifunit >= 0)
-               notice("Connect: %s <--> %s", ifname, ppp_devnam);
-       else
-               notice("Starting negotiation on %s", ppp_devnam);
        gettimeofday(&start_time, NULL);
        script_unsetenv("CONNECT_TIME");
        script_unsetenv("BYTES_SENT");
        script_unsetenv("BYTES_RCVD");
-       lcp_lowerup(0);
 
-       add_fd(fd_ppp);
        lcp_open(0);            /* Start protocol */
-       status = EXIT_NEGOTIATION_FAILED;
-       new_phase(PHASE_ESTABLISH);
        while (phase != PHASE_DEAD) {
            handle_events();
            get_input();
-           if (kill_link)
+           if (kill_link) {
+               bundle_terminating = 1;
                lcp_close(0, "User request");
+               if (phase == PHASE_MASTER)
+                   mp_bundle_terminated();
+           }
            if (open_ccp_flag) {
                if (phase == PHASE_NETWORK || phase == PHASE_RUNNING) {
                    ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
@@ -586,54 +554,6 @@ main(argc, argv)
            }
        }
 
-       print_link_stats();
-
-       /*
-        * Delete pid file before disestablishing ppp.  Otherwise it
-        * can happen that another pppd gets the same unit and then
-        * we delete its pid file.
-        */
-       if (!demand) {
-           if (pidfilename[0] != 0
-               && unlink(pidfilename) < 0 && errno != ENOENT)
-               warn("unable to delete pid file %s: %m", pidfilename);
-           pidfilename[0] = 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.
-        */
-       remove_fd(fd_ppp);
-       clean_check();
-       the_channel->disestablish_ppp(devfd);
-       fd_ppp = -1;
-       if (!hungup)
-           lcp_lowerdown(0);
-       if (!demand)
-           script_unsetenv("IFNAME");
-
-       /*
-        * Run disconnector script, if requested.
-        * XXX we may not be able to do this if the line has hung up!
-        */
-    disconnect:
-       new_phase(PHASE_DISCONNECT);
-       if (the_channel->disconnect)
-           the_channel->disconnect();
-
-    fail:
-       if (the_channel->cleanup)
-           (*the_channel->cleanup)();
-
-       if (!demand) {
-           if (pidfilename[0] != 0
-               && unlink(pidfilename) < 0 && errno != ENOENT)
-               warn("unable to delete pid file %s: %m", pidfilename);
-           pidfilename[0] = 0;
-       }
-
        if (!persist || (maxfail > 0 && unsuccess >= maxfail))
            break;
 
@@ -897,7 +817,7 @@ create_pidfile(pid)
     }
 }
 
-static void
+void
 create_linkpidfile(pid)
     int pid;
 {
@@ -919,6 +839,19 @@ create_linkpidfile(pid)
     }
 }
 
+/*
+ * remove_pidfile - remove our pid files
+ */
+void remove_pidfiles()
+{
+    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+       warn("unable to delete pid file %s: %m", pidfilename);
+    pidfilename[0] = 0;
+    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
+       warn("unable to delete pid file %s: %m", linkpidfile);
+    linkpidfile[0] = 0;
+}
+
 /*
  * holdoff_end - called via a timeout when the holdoff period ends.
  */
@@ -1031,6 +964,11 @@ get_input()
        return;
 
     if (len == 0) {
+       if (bundle_eof && multilink_master) {
+           notice("Last channel has disconnected");
+           mp_bundle_terminated();
+           return;
+       }
        notice("Modem hangup");
        hungup = 1;
        status = EXIT_HANGUP;
@@ -1159,7 +1097,8 @@ void
 die(status)
     int status;
 {
-    print_link_stats();
+    if (!doing_multilink || multilink_master)
+       print_link_stats();
     cleanup();
     notify(exitnotify, status);
     syslog(LOG_INFO, "Exit.");
@@ -1179,13 +1118,7 @@ cleanup()
        the_channel->disestablish_ppp(devfd);
     if (the_channel->cleanup)
        (*the_channel->cleanup)();
-
-    if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
-       warn("unable to delete pid file %s: %m", pidfilename);
-    pidfilename[0] = 0;
-    if (linkpidfile[0] != 0 && unlink(linkpidfile) < 0 && errno != ENOENT)
-       warn("unable to delete pid file %s: %m", linkpidfile);
-    linkpidfile[0] = 0;
+    remove_pidfiles();
 
 #ifdef USE_TDB
     if (pppdb != NULL)
@@ -2004,7 +1937,7 @@ update_db_entry()
     vlen = 0;
     for (i = 0; (p = script_env[i]) != 0; ++i)
        vlen += strlen(p) + 1;
-    vbuf = malloc(vlen);
+    vbuf = malloc(vlen + 1);
     if (vbuf == 0)
        novm("database entry");
     q = vbuf;
index dd6d23f699496420221230d49e3d63c0354c4733..7444162e66eef0dc3d43c52f78569ef013ce59cd 100644 (file)
@@ -34,6 +34,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
 #include "pppd.h"
 #include "fsm.h"
 
 bool endpoint_specified;       /* user gave explicit endpoint discriminator */
 char *bundle_id;               /* identifier for our bundle */
+char *blinks_id;               /* key for the list of links */
+bool doing_multilink;          /* multilink was enabled and agreed to */
+bool multilink_master;         /* we own the multilink bundle */
 
 extern TDB_CONTEXT *pppdb;
 extern char db_key[];
 
+static void make_bundle_links __P((int append));
+static void remove_bundle_link __P((void));
+static void iterate_bundle_links __P((void (*func) __P((char *))));
+
 static int get_default_epdisc __P((struct epdisc *));
 static int parse_num __P((char *str, const char *key, int *valp));
 static int owns_unit __P((TDB_DATA pid, int unit));
@@ -71,6 +79,7 @@ mp_check_options()
        lcp_options *wo = &lcp_wantoptions[0];
        lcp_options *ao = &lcp_allowoptions[0];
 
+       doing_multilink = 0;
        if (!multilink)
                return;
        /* if we're doing multilink, we have to negotiate MRRU */
@@ -103,6 +112,18 @@ mp_join_bundle()
        char *p;
        TDB_DATA key, pid, rec;
 
+       if (doing_multilink) {
+               /* have previously joined a bundle */
+               if (!go->neg_mrru || !ho->neg_mrru) {
+                       notice("oops, didn't get multilink on renegotiation");
+                       lcp_close(0, "multilink required");
+                       return 0;
+               }
+               /* XXX should check the peer_authname and ho->endpoint
+                  are the same as previously */
+               return 0;
+       }
+
        if (!go->neg_mrru || !ho->neg_mrru) {
                /* not doing multilink */
                if (go->neg_mrru)
@@ -122,6 +143,8 @@ mp_join_bundle()
                return 0;
        }
 
+       doing_multilink = 1;
+
        /*
         * Find the appropriate bundle or join a new one.
         * First we make up a name for the bundle.
@@ -147,6 +170,13 @@ mp_join_bundle()
        if (bundle_name)
                p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
 
+       /* Make the key for the list of links belonging to the bundle */
+       l = p - bundle_id;
+       blinks_id = malloc(l + 7);
+       if (blinks_id == NULL)
+               novm("bundle links key");
+       slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
        /*
         * For demand mode, we only need to configure the bundle
         * and attach the link.
@@ -170,8 +200,10 @@ mp_join_bundle()
        if (pid.dptr != NULL) {
                /* bundle ID exists, see if the pppd record exists */
                rec = tdb_fetch(pppdb, pid);
-               if (rec.dptr != NULL) {
-                       /* it is, parse the interface number */
+               if (rec.dptr != NULL && rec.dsize > 0) {
+                       /* make sure the string is null-terminated */
+                       rec.dptr[rec.dsize-1] = 0;
+                       /* parse the interface number */
                        parse_num(rec.dptr, "IFNAME=ppp", &unit);
                        /* check the pid value */
                        if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
@@ -188,6 +220,7 @@ mp_join_bundle()
                if (bundle_attach(unit)) {
                        set_ifunit(0);
                        script_setenv("BUNDLE", bundle_id + 7, 0);
+                       make_bundle_links(1);
                        tdb_writeunlock(pppdb);
                        info("Link attached to %s", ifname);
                        return 1;
@@ -200,11 +233,157 @@ mp_join_bundle()
        set_ifunit(1);
        netif_set_mtu(0, mtu);
        script_setenv("BUNDLE", bundle_id + 7, 1);
+       make_bundle_links(0);
        tdb_writeunlock(pppdb);
        info("New bundle %s created", ifname);
+       multilink_master = 1;
        return 0;
 }
 
+void mp_exit_bundle()
+{
+       tdb_writelock(pppdb);
+       remove_bundle_link();
+       tdb_writeunlock(pppdb);
+}
+
+static void sendhup(char *str)
+{
+       int pid;
+
+       if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+               if (debug)
+                       dbglog("sending SIGHUP to process %d", pid);
+               kill(pid, SIGHUP);
+       }
+}
+
+void mp_bundle_terminated()
+{
+       TDB_DATA key;
+
+       bundle_terminating = 1;
+       upper_layers_down(0);
+       notice("Connection terminated.");
+       print_link_stats();
+       if (!demand) {
+               remove_pidfiles();
+               script_unsetenv("IFNAME");
+       }
+
+       tdb_writelock(pppdb);
+       destroy_bundle();
+       iterate_bundle_links(sendhup);
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       tdb_delete(pppdb, key);
+       tdb_writeunlock(pppdb);
+       
+new_phase(PHASE_DEAD);
+}
+
+static void make_bundle_links(int append)
+{
+       TDB_DATA key, rec;
+       char *p;
+       char entry[32];
+       int l;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       slprintf(entry, sizeof(entry), "%s;", db_key);
+       p = entry;
+       if (append) {
+               rec = tdb_fetch(pppdb, key);
+               if (rec.dptr != NULL && rec.dsize > 0) {
+                       rec.dptr[rec.dsize-1] = 0;
+                       if (strstr(rec.dptr, db_key) != NULL) {
+                               /* already in there? strange */
+                               warn("link entry already exists in tdb");
+                               return;
+                       }
+                       l = rec.dsize + strlen(entry);
+                       p = malloc(l);
+                       if (p == NULL)
+                               novm("bundle link list");
+                       slprintf(p, l, "%s%s", rec.dptr, entry);
+               } else {
+                       warn("bundle link list not found");
+               }
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+       }
+       rec.dptr = p;
+       rec.dsize = strlen(p) + 1;
+       if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+               error("couldn't %s bundle link list",
+                     append? "update": "create");
+       if (p != entry)
+               free(p);
+}
+
+static void remove_bundle_link()
+{
+       TDB_DATA key, rec;
+       char entry[32];
+       char *p, *q;
+       int l;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       slprintf(entry, sizeof(entry), "%s;", db_key);
+
+       rec = tdb_fetch(pppdb, key);
+       if (rec.dptr == NULL || rec.dsize <= 0) {
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+               return;
+       }
+       rec.dptr[rec.dsize-1] = 0;
+       p = strstr(rec.dptr, entry);
+       if (p != NULL) {
+               q = p + strlen(entry);
+               l = strlen(q) + 1;
+               memmove(p, q, l);
+               rec.dsize = p - rec.dptr + l;
+               if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+                       error("couldn't update bundle link list (removal)");
+       }
+       free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+       TDB_DATA key, rec, pp;
+       char *p, *q;
+
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       rec = tdb_fetch(pppdb, key);
+       if (rec.dptr == NULL || rec.dsize <= 0) {
+               error("bundle link list not found (iterating list)");
+               if (rec.dptr != NULL)
+                       free(rec.dptr);
+               return;
+       }
+       p = rec.dptr;
+       p[rec.dsize-1] = 0;
+       while ((q = strchr(p, ';')) != NULL) {
+               *q = 0;
+               key.dptr = p;
+               key.dsize = q - p;
+               pp = tdb_fetch(pppdb, key);
+               if (pp.dptr != NULL && pp.dsize > 0) {
+                       pp.dptr[pp.dsize-1] = 0;
+                       func(pp.dptr);
+               }
+               if (pp.dptr != NULL)
+                       free(pp.dptr);
+               p = q + 1;
+       }
+       free(rec.dptr);
+}
+
 static int
 parse_num(str, key, valp)
      char *str;
index f4f53817e1e3f7464c61c3cb74a3d4a28019ce4c..de3a6a18515d398eaf5bcf7c7d8a479959b095e5 100644 (file)
@@ -39,7 +39,7 @@
  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $Id: pppd.h,v 1.86 2004/11/06 05:42:29 paulus Exp $
+ * $Id: pppd.h,v 1.87 2004/11/12 10:30:51 paulus Exp $
  */
 
 /*
@@ -215,6 +215,8 @@ extern int  ifunit;         /* Interface unit number */
 extern char    ifname[];       /* Interface name */
 extern char    hostname[];     /* Our hostname */
 extern u_char  outpacket_buf[]; /* Buffer for outgoing packets */
+extern int     devfd;          /* fd of underlying device */
+extern int     fd_ppp;         /* fd for talking PPP */
 extern int     phase;          /* Current state of link - see values below */
 extern int     baud_rate;      /* Current link speed in bits/sec */
 extern char    *progname;      /* Name of this program */
@@ -246,6 +248,11 @@ extern int      ppp_session_number; /* Session number (eg PPPoE session) */
 extern int     fd_devnull;     /* fd open to /dev/null */
 
 extern int     listen_time;    /* time to listen first (ms) */
+extern bool    doing_multilink;
+extern bool    multilink_master;
+extern bool    bundle_eof;
+extern bool    bundle_terminating;
+
 extern struct notifier *pidchange;   /* for notifications of pid changing */
 extern struct notifier *phasechange; /* for notifications of phase changes */
 extern struct notifier *exitnotify;  /* for notification that we're exiting */
@@ -375,6 +382,7 @@ extern int  option_priority;        /* priority of current options */
 #define PHASE_TERMINATE                9
 #define PHASE_DISCONNECT       10
 #define PHASE_HOLDOFF          11
+#define PHASE_MASTER           12
 
 /*
  * The following struct gives the addresses of procedures to call
@@ -484,6 +492,7 @@ void remove_notifier __P((struct notifier **, notify_func, void *));
 void notify __P((struct notifier *, int));
 int  ppp_send_config __P((int, int, u_int32_t, int, int));
 int  ppp_recv_config __P((int, int, u_int32_t, int, int));
+void remove_pidfiles __P((void));
 
 /* Procedures exported from tty.c. */
 void tty_init __P((void));
@@ -515,6 +524,7 @@ ssize_t complete_read __P((int, void *, size_t));
 void link_required __P((int));   /* we are starting to use the link */
 void link_terminated __P((int));  /* we are finished with the link */
 void link_down __P((int));       /* the LCP layer has left the Opened state */
+void upper_layers_down __P((int));/* take all NCPs down */
 void link_established __P((int)); /* the link is up; authenticate now */
 void start_networks __P((int));   /* start all the network control protos */
 void continue_networks __P((int)); /* start network [ip, etc] control protos */
@@ -554,10 +564,19 @@ int  loop_chars __P((unsigned char *, int)); /* process chars from loopback */
 int  loop_frame __P((unsigned char *, int)); /* should we bring link up? */
 
 /* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
 void mp_check_options __P((void)); /* Check multilink-related options */
 int  mp_join_bundle __P((void));  /* join our link to an appropriate bundle */
+void mp_exit_bundle __P((void));  /* have disconnected our link from bundle */
+void mp_bundle_terminated __P((void));
 char *epdisc_to_str __P((struct epdisc *)); /* string from endpoint discrim. */
 int  str_to_epdisc __P((struct epdisc *, char *)); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated() /* nothing */
+#define mp_exit_bundle()       /* nothing */
+#define doing_multilink                0
+#define multilink_master       0
+#endif
 
 /* Procedures exported from sys-*.c */
 void sys_init __P((void));     /* Do system-dependent initialization */
@@ -574,6 +593,7 @@ int  generic_establish_ppp __P((int dev_fd)); /* Make a ppp interface */
 void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
 int  bundle_attach __P((int)); /* Attach link to existing bundle */
 void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
+void destroy_bundle __P((void)); /* Tell driver to destroy bundle */
 void clean_check __P((void));  /* Check if line was 8-bit clean */
 void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
 void restore_tty __P((int));   /* Restore port's original parameters */
index 73843e745bae68685459a7e696996f9e83f20c23..e963c79786097d2957ac65b6ca2323d58edd44de 100644 (file)
@@ -593,7 +593,7 @@ void generic_disestablish_ppp(int dev_fd)
        if (demand) {
            modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
            looped = 1;
-       } else if (ppp_dev_fd >= 0) {
+       } else if (!doing_multilink && ppp_dev_fd >= 0) {
            close(ppp_dev_fd);
            remove_fd(ppp_dev_fd);
            ppp_dev_fd = -1;
@@ -712,6 +712,18 @@ int bundle_attach(int ifnum)
        return 1;
 }
 
+/*
+ * destroy_bundle - tell the driver to destroy our bundle.
+ */
+void destroy_bundle(void)
+{
+       if (ppp_dev_fd >= 0) {
+               close(ppp_dev_fd);
+               remove_fd(ppp_dev_fd);
+               ppp_dev_fd = -1;
+       }
+}
+
 /********************************************************************
  *
  * clean_check - Fetch the flags for the device and generate
@@ -1086,15 +1098,21 @@ int read_packet (unsigned char *buf)
        if (nr < 0 && errno == ENXIO)
            return 0;
     }
-    if (nr < 0 && new_style_driver && ppp_dev_fd >= 0) {
+    if (nr < 0 && new_style_driver && ppp_dev_fd >= 0 && !bundle_eof) {
        /* N.B. we read ppp_fd first since LCP packets come in there. */
        nr = read(ppp_dev_fd, buf, len);
        if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
            && errno != EIO && errno != EINTR)
            error("read /dev/ppp: %m");
        if (nr < 0 && errno == ENXIO)
-           return 0;
+           nr = 0;
+       if (nr == 0 && doing_multilink) {
+           remove_fd(ppp_dev_fd);
+           bundle_eof = 1;
+       }
     }
+    if (new_style_driver && ppp_fd < 0 && ppp_dev_fd < 0)
+       nr = 0;
     return (new_style_driver && nr > 0)? nr+2: nr;
 }