123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- /*
- * multilink.c - support routines for multilink.
- *
- * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
- *
- * 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>
- #include <stdlib.h>
- #include <netdb.h>
- #include <errno.h>
- #include <signal.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include "pppd.h"
- #include "fsm.h"
- #include "lcp.h"
- #include "tdb.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));
- #define set_ip_epdisc(ep, addr) do { \
- ep->length = 4; \
- ep->value[0] = addr >> 24; \
- ep->value[1] = addr >> 16; \
- ep->value[2] = addr >> 8; \
- ep->value[3] = addr; \
- } while (0)
- #define LOCAL_IP_ADDR(addr) \
- (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \
- || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \
- || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
- #define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH)
- void
- 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 */
- if (!wo->neg_mrru) {
- /* mrru not specified, default to mru */
- wo->mrru = wo->mru;
- wo->neg_mrru = 1;
- }
- ao->mrru = ao->mru;
- ao->neg_mrru = 1;
- if (!wo->neg_endpoint && !noendpoint) {
- /* get a default endpoint value */
- wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
- }
- }
- /*
- * Make a new bundle or join us to an existing bundle
- * if we are doing multilink.
- */
- int
- 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, 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.
- * The length estimate is worst-case assuming every
- * character has to be quoted.
- */
- l = 4 * strlen(peer_authname) + 10;
- if (ho->neg_endpoint)
- l += 3 * ho->endpoint.length + 8;
- if (bundle_name)
- l += 3 * strlen(bundle_name) + 2;
- bundle_id = malloc(l);
- if (bundle_id == 0)
- novm("bundle identifier");
- p = bundle_id;
- p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
- if (ho->neg_endpoint || bundle_name)
- *p++ = '/';
- if (ho->neg_endpoint)
- p += slprintf(p, bundle_id+l-p, "%s",
- epdisc_to_str(&ho->endpoint));
- 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.
- */
- 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;
- }
- /*
- * Check if the bundle ID is already in the database.
- */
- unit = -1;
- 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 && 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)
- || !process_exists(pppd_pid)
- || !owns_unit(pid, unit))
- unit = -1;
- free(rec.dptr);
- }
- free(pid.dptr);
- }
- if (unit >= 0) {
- /* attach to existing unit */
- if (bundle_attach(unit)) {
- set_ifunit(0);
- script_setenv("BUNDLE", bundle_id + 7, 0);
- make_bundle_links(1);
- unlock_db();
- info("Link attached to %s", ifname);
- return 1;
- }
- /* attach failed because bundle doesn't exist */
- }
- /* 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);
- 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;
- const char *key;
- int *valp;
- {
- char *p, *endp;
- int i;
- p = strstr(str, key);
- if (p != 0) {
- p += strlen(key);
- i = strtol(p, &endp, 10);
- if (endp != p && (*endp == 0 || *endp == ';')) {
- *valp = i;
- return 1;
- }
- }
- return 0;
- }
- /*
- * Check whether the pppd identified by `key' still owns ppp unit `unit'.
- */
- static int
- owns_unit(key, unit)
- TDB_DATA key;
- int unit;
- {
- char ifkey[32];
- TDB_DATA kd, vd;
- int ret = 0;
- slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
- kd.dptr = ifkey;
- kd.dsize = strlen(ifkey);
- vd = tdb_fetch(pppdb, kd);
- if (vd.dptr != NULL) {
- ret = vd.dsize == key.dsize
- && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
- free(vd.dptr);
- }
- return ret;
- }
- static int
- get_default_epdisc(ep)
- 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) {
- ep->class = EPD_MAC;
- ep->length = 6;
- return 1;
- }
- /* see if our hostname corresponds to a reasonable IP address */
- hp = gethostbyname(hostname);
- if (hp != NULL) {
- addr = *(u_int32_t *)hp->h_addr;
- if (!bad_ip_adrs(addr)) {
- addr = ntohl(addr);
- if (!LOCAL_IP_ADDR(addr)) {
- ep->class = EPD_IP;
- set_ip_epdisc(ep, addr);
- return 1;
- }
- }
- }
- return 0;
- }
- /*
- * epdisc_to_str - make a printable string from an endpoint discriminator.
- */
- static char *endp_class_names[] = {
- "null", "local", "IP", "MAC", "magic", "phone"
- };
- char *
- epdisc_to_str(ep)
- struct epdisc *ep;
- {
- static char str[MAX_ENDP_LEN*3+8];
- u_char *p = ep->value;
- int i, mask = 0;
- char *q, c, c2;
- if (ep->class == EPD_NULL && ep->length == 0)
- return "null";
- if (ep->class == EPD_IP && ep->length == 4) {
- u_int32_t addr;
- GETLONG(addr, p);
- slprintf(str, sizeof(str), "IP:%I", htonl(addr));
- return str;
- }
- c = ':';
- c2 = '.';
- if (ep->class == EPD_MAC && ep->length == 6)
- c2 = ':';
- else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
- mask = 3;
- q = str;
- if (ep->class <= EPD_PHONENUM)
- q += slprintf(q, sizeof(str)-1, "%s",
- endp_class_names[ep->class]);
- else
- q += slprintf(q, sizeof(str)-1, "%d", ep->class);
- c = ':';
- for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
- if ((i & mask) == 0) {
- *q++ = c;
- c = c2;
- }
- q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
- }
- return str;
- }
- static int hexc_val(int c)
- {
- if (c >= 'a')
- return c - 'a' + 10;
- if (c >= 'A')
- return c - 'A' + 10;
- return c - '0';
- }
- int
- str_to_epdisc(ep, str)
- struct epdisc *ep;
- char *str;
- {
- int i, l;
- char *p, *endp;
- for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
- int sl = strlen(endp_class_names[i]);
- if (strncasecmp(str, endp_class_names[i], sl) == 0) {
- str += sl;
- break;
- }
- }
- if (i > EPD_PHONENUM) {
- /* not a class name, try a decimal class number */
- i = strtol(str, &endp, 10);
- if (endp == str)
- return 0; /* can't parse class number */
- str = endp;
- }
- ep->class = i;
- if (*str == 0) {
- ep->length = 0;
- return 1;
- }
- if (*str != ':' && *str != '.')
- return 0;
- ++str;
- if (i == EPD_IP) {
- u_int32_t addr;
- i = parse_dotted_ip(str, &addr);
- if (i == 0 || str[i] != 0)
- return 0;
- set_ip_epdisc(ep, addr);
- return 1;
- }
- if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
- ep->length = 6;
- return 1;
- }
- p = str;
- for (l = 0; l < MAX_ENDP_LEN; ++l) {
- if (*str == 0)
- break;
- if (p <= str)
- for (p = str; isxdigit(*p); ++p)
- ;
- i = p - str;
- if (i == 0)
- return 0;
- ep->value[l] = hexc_val(*str++);
- if ((i & 1) == 0)
- ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
- if (*str == ':' || *str == '.')
- ++str;
- }
- if (*str != 0 || (ep->class == EPD_MAC && l != 6))
- return 0;
- ep->length = l;
- return 1;
- }
|