]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/multilink.c
Fix authentication on second time around with multilink and persist
[ppp.git] / pppd / multilink.c
index da8b33a40c418b638eafb206bdcf818f6bba36be..135cab0b42ad419d1c4297240cf464ca6b83b14f 100644 (file)
@@ -1,17 +1,31 @@
 /*
  * multilink.c - support routines for multilink.
  *
- * Copyright (c) 2000 Paul Mackerras.
- * All rights reserved.
+ * Copyright (c) 2000-2002 Paul Mackerras. 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.  The name of the author 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #include <string.h>
 #include <ctype.h>
@@ -20,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));
@@ -49,7 +71,7 @@ static int owns_unit __P((TDB_DATA pid, int unit));
         || ((addr) & 0xfff00000) == 0xac100000         /* 172.16.x.x */  \
         || ((addr) & 0xffff0000) == 0xc0a80000)        /* 192.168.x.x */
 
-#define process_exists(n)      (kill(0, (n)) == 0 || errno != ESRCH)
+#define process_exists(n)      (kill((n), 0) == 0 || errno != ESRCH)
 
 void
 mp_check_options()
@@ -57,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 */
@@ -71,9 +94,6 @@ mp_check_options()
        if (!wo->neg_endpoint && !noendpoint) {
                /* get a default endpoint value */
                wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
-               if (wo->neg_endpoint)
-                       dbglog("using default endpoint %s",
-                              epdisc_to_str(&wo->endpoint));
        }
 }
 
@@ -86,25 +106,45 @@ mp_join_bundle()
 {
        lcp_options *go = &lcp_gotoptions[0];
        lcp_options *ho = &lcp_hisoptions[0];
+       lcp_options *ao = &lcp_allowoptions[0];
        int unit, pppd_pid;
-       int l;
+       int l, mtu;
        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)
                        notice("oops, multilink negotiated only for receive");
+               mtu = ho->neg_mru? ho->mru: PPP_MRU;
+               if (mtu > ao->mru)
+                       mtu = ao->mru;
                if (demand) {
                        /* already have a bundle */
                        cfg_bundle(0, 0, 0, 0);
+                       netif_set_mtu(0, mtu);
                        return 0;
                }
                make_new_bundle(0, 0, 0, 0);
                set_ifunit(1);
+               netif_set_mtu(0, mtu);
                return 0;
        }
 
+       doing_multilink = 1;
+
        /*
         * Find the appropriate bundle or join a new one.
         * First we make up a name for the bundle.
@@ -129,14 +169,22 @@ mp_join_bundle()
                              epdisc_to_str(&ho->endpoint));
        if (bundle_name)
                p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
-       dbglog("bundle_id = %s", bundle_id+7);
+
+       /* 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.
         */
+       mtu = MIN(ho->mrru, ao->mru);
        if (demand) {
                cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+               netif_set_mtu(0, mtu);
                script_setenv("BUNDLE", bundle_id + 7, 1);
                return 0;
        }
@@ -145,15 +193,17 @@ mp_join_bundle()
         * Check if the bundle ID is already in the database.
         */
        unit = -1;
-       tdb_writelock(pppdb);
+       lock_db();
        key.dptr = bundle_id;
        key.dsize = p - bundle_id;
        pid = tdb_fetch(pppdb, key);
        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)
@@ -170,7 +220,8 @@ mp_join_bundle()
                if (bundle_attach(unit)) {
                        set_ifunit(0);
                        script_setenv("BUNDLE", bundle_id + 7, 0);
-                       tdb_writeunlock(pppdb);
+                       make_bundle_links(1);
+                       unlock_db();
                        info("Link attached to %s", ifname);
                        return 1;
                }
@@ -180,12 +231,162 @@ mp_join_bundle()
        /* we have to make a new bundle */
        make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
        set_ifunit(1);
+       netif_set_mtu(0, mtu);
        script_setenv("BUNDLE", bundle_id + 7, 1);
-       tdb_writeunlock(pppdb);
+       make_bundle_links(0);
+       unlock_db();
        info("New bundle %s created", ifname);
+       multilink_master = 1;
        return 0;
 }
 
+void mp_exit_bundle()
+{
+       lock_db();
+       remove_bundle_link();
+       unlock_db();
+}
+
+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");
+       }
+
+       lock_db();
+       destroy_bundle();
+       iterate_bundle_links(sendhup);
+       key.dptr = blinks_id;
+       key.dsize = strlen(blinks_id);
+       tdb_delete(pppdb, key);
+       unlock_db();
+
+       new_phase(PHASE_DEAD);
+
+       doing_multilink = 0;
+       multilink_master = 0;
+}
+
+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;
@@ -360,12 +561,10 @@ str_to_epdisc(ep, str)
                if (i == 0 || str[i] != 0)
                        return 0;
                set_ip_epdisc(ep, addr);
-               dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
                return 1;
        }
        if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
                ep->length = 6;
-               dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
                return 1;
        }
 
@@ -388,7 +587,6 @@ str_to_epdisc(ep, str)
        if (*str != 0 || (ep->class == EPD_MAC && l != 6))
                return 0;
        ep->length = l;
-       dbglog("str_to_epdisc -> %s", epdisc_to_str(ep));
        return 1;
 }