]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/multilink.c
CI: Updated the 'checkout' actions that were using Node.js 16 to Node.js 20. (#489)
[ppp.git] / pppd / multilink.c
index da8b33a40c418b638eafb206bdcf818f6bba36be..b44d2bbe5dd4ecff2678dc065430b46da82dabed 100644 (file)
@@ -1,18 +1,37 @@
 /*
  * 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@ozlabs.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.
  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include <string.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <signal.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
-#include "pppd.h"
+#include "pppd-private.h"
 #include "fsm.h"
 #include "lcp.h"
 #include "tdb.h"
+#include "multilink.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 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));
+static void make_bundle_links(int append);
+static void remove_bundle_link(void);
+static void iterate_bundle_links(void (*func)(char *));
+
+static int get_default_epdisc(struct epdisc *);
+static int parse_num(char *str, const char *key, int *valp);
+static int owns_unit(TDB_DATA pid, int unit);
 
 #define set_ip_epdisc(ep, addr) do {   \
        ep->length = 4;                 \
@@ -49,14 +77,27 @@ 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)
+
+multilink_join_hook_fn *multilink_join_hook = NULL;
+
+bool mp_master()
+{
+    return multilink_master;
+}
+
+bool mp_on()
+{
+    return doing_multilink;
+}
 
 void
-mp_check_options()
+mp_check_options(void)
 {
        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 +112,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));
        }
 }
 
@@ -82,29 +120,49 @@ mp_check_options()
  * if we are doing multilink.
  */
 int
-mp_join_bundle()
+mp_join_bundle(void)
 {
        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);
+                       ppp_set_mtu(0, mtu);
                        return 0;
                }
                make_new_bundle(0, 0, 0, 0);
                set_ifunit(1);
+               ppp_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,15 +187,23 @@ 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);
-               script_setenv("BUNDLE", bundle_id + 7, 1);
+               ppp_set_mtu(0, mtu);
+               ppp_script_setenv("BUNDLE", bundle_id + 7, 1);
                return 0;
        }
 
@@ -145,16 +211,18 @@ 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 */
-                       parse_num(rec.dptr, "IFNAME=ppp", &unit);
+               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, "UNIT=", &unit);
                        /* check the pid value */
                        if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
                            || !process_exists(pppd_pid)
@@ -169,8 +237,9 @@ mp_join_bundle()
                /* attach to existing unit */
                if (bundle_attach(unit)) {
                        set_ifunit(0);
-                       script_setenv("BUNDLE", bundle_id + 7, 0);
-                       tdb_writeunlock(pppdb);
+                       ppp_script_setenv("BUNDLE", bundle_id + 7, 0);
+                       make_bundle_links(1);
+                       unlock_db();
                        info("Link attached to %s", ifname);
                        return 1;
                }
@@ -180,17 +249,164 @@ 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);
-       script_setenv("BUNDLE", bundle_id + 7, 1);
-       tdb_writeunlock(pppdb);
+       ppp_set_mtu(0, mtu);
+       ppp_script_setenv("BUNDLE", bundle_id + 7, 1);
+       make_bundle_links(0);
+       unlock_db();
        info("New bundle %s created", ifname);
+       multilink_master = 1;
        return 0;
 }
 
+void mp_exit_bundle(void)
+{
+       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(void)
+{
+       TDB_DATA key;
+
+       bundle_terminating = 1;
+       upper_layers_down(0);
+       notice("Connection terminated.");
+       print_link_stats();
+       if (!demand) {
+               remove_pidfiles();
+               ppp_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(void)
+{
+       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;
-     const char *key;
-     int *valp;
+parse_num(char *str, const char *key, int *valp)
 {
        char *p, *endp;
        int i;
@@ -211,15 +427,13 @@ parse_num(str, key, valp)
  * Check whether the pppd identified by `key' still owns ppp unit `unit'.
  */
 static int
-owns_unit(key, unit)
-     TDB_DATA key;
-     int unit;
+owns_unit(TDB_DATA key, int unit)
 {
        char ifkey[32];
        TDB_DATA kd, vd;
        int ret = 0;
 
-       slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+       slprintf(ifkey, sizeof(ifkey), "UNIT=%d", unit);
        kd.dptr = ifkey;
        kd.dsize = strlen(ifkey);
        vd = tdb_fetch(pppdb, kd);
@@ -232,16 +446,13 @@ owns_unit(key, unit)
 }
 
 static int
-get_default_epdisc(ep)
-     struct epdisc *ep;
+get_default_epdisc(struct epdisc *ep)
 {
-       char *p;
        struct hostent *hp;
        u_int32_t addr;
 
        /* First try for an ethernet MAC address */
-       p = get_first_ethernet();
-       if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
+       if (get_first_ether_hwaddr(ep->value) >= 0) {
                ep->class = EPD_MAC;
                ep->length = 6;
                return 1;
@@ -251,7 +462,7 @@ get_default_epdisc(ep)
        hp = gethostbyname(hostname);
        if (hp != NULL) {
                addr = *(u_int32_t *)hp->h_addr;
-               if (!bad_ip_adrs(addr)) {
+               if (!ppp_bad_ip_addr(addr)) {
                        addr = ntohl(addr);
                        if (!LOCAL_IP_ADDR(addr)) {
                                ep->class = EPD_IP;
@@ -273,8 +484,7 @@ static char *endp_class_names[] = {
 };
 
 char *
-epdisc_to_str(ep)
-     struct epdisc *ep;
+epdisc_to_str(struct epdisc *ep)
 {
        static char str[MAX_ENDP_LEN*3+8];
        u_char *p = ep->value;
@@ -324,9 +534,7 @@ static int hexc_val(int c)
 }
 
 int
-str_to_epdisc(ep, str)
-     struct epdisc *ep;
-     char *str;
+str_to_epdisc(struct epdisc *ep, char *str)
 {
        int i, l;
        char *p, *endp;
@@ -360,12 +568,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 +594,5 @@ 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;
 }
-