Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_multilink.c Source File

lwip_multilink.c

00001 /*
00002  * multilink.c - support routines for multilink.
00003  *
00004  * Copyright (c) 2000-2002 Paul Mackerras. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  *
00013  * 2. The name(s) of the authors of this software must not be used to
00014  *    endorse or promote products derived from this software without
00015  *    prior written permission.
00016  *
00017  * 3. Redistributions of any form whatsoever must retain the following
00018  *    acknowledgment:
00019  *    "This product includes software developed by Paul Mackerras
00020  *     <paulus@samba.org>".
00021  *
00022  * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
00023  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00024  * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
00025  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00026  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
00027  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
00028  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00029  */
00030 
00031 #include "netif/ppp/ppp_opts.h"
00032 #if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */
00033 
00034 /* Multilink support
00035  *
00036  * Multilink uses Samba TDB (Trivial Database Library), which
00037  * we cannot port, because it needs a filesystem.
00038  *
00039  * We have to choose between doing a memory-shared TDB-clone,
00040  * or dropping multilink support at all.
00041  */
00042 
00043 #include <string.h>
00044 #include <ctype.h>
00045 #include <stdlib.h>
00046 #include <netdb.h>
00047 #include <errno.h>
00048 #include <signal.h>
00049 #include <netinet/in.h>
00050 #include <unistd.h>
00051 
00052 #include "netif/ppp/ppp_impl.h"
00053 
00054 #include "netif/ppp/fsm.h"
00055 #include "netif/ppp/lcp.h"
00056 #include "netif/ppp/tdb.h"
00057 
00058 bool endpoint_specified;    /* user gave explicit endpoint discriminator */
00059 char *bundle_id;        /* identifier for our bundle */
00060 char *blinks_id;        /* key for the list of links */
00061 bool doing_multilink;       /* multilink was enabled and agreed to */
00062 bool multilink_master;      /* we own the multilink bundle */
00063 
00064 extern TDB_CONTEXT *pppdb;
00065 extern char db_key[];
00066 
00067 static void make_bundle_links (int append);
00068 static void remove_bundle_link (void);
00069 static void iterate_bundle_links (void (*func) (char *));
00070 
00071 static int get_default_epdisc (struct epdisc *);
00072 static int parse_num (char *str, const char *key, int *valp);
00073 static int owns_unit (TDB_DATA pid, int unit);
00074 
00075 #define set_ip_epdisc(ep, addr) do {    \
00076     ep->length = 4;         \
00077     ep->value[0] = addr >> 24;  \
00078     ep->value[1] = addr >> 16;  \
00079     ep->value[2] = addr >> 8;   \
00080     ep->value[3] = addr;        \
00081 } while (0)
00082 
00083 #define LOCAL_IP_ADDR(addr)                       \
00084     (((addr) & 0xff000000) == 0x0a000000        /* 10.x.x.x */    \
00085      || ((addr) & 0xfff00000) == 0xac100000     /* 172.16.x.x */  \
00086      || ((addr) & 0xffff0000) == 0xc0a80000)    /* 192.168.x.x */
00087 
00088 #define process_exists(n)   (kill((n), 0) == 0 || errno != ESRCH)
00089 
00090 void
00091 mp_check_options()
00092 {
00093     lcp_options *wo = &lcp_wantoptions[0];
00094     lcp_options *ao = &lcp_allowoptions[0];
00095 
00096     doing_multilink = 0;
00097     if (!multilink)
00098         return;
00099     /* if we're doing multilink, we have to negotiate MRRU */
00100     if (!wo->neg_mrru) {
00101         /* mrru not specified, default to mru */
00102         wo->mrru = wo->mru;
00103         wo->neg_mrru = 1;
00104     }
00105     ao->mrru = ao->mru;
00106     ao->neg_mrru = 1;
00107 
00108     if (!wo->neg_endpoint && !noendpoint) {
00109         /* get a default endpoint value */
00110         wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
00111     }
00112 }
00113 
00114 /*
00115  * Make a new bundle or join us to an existing bundle
00116  * if we are doing multilink.
00117  */
00118 int
00119 mp_join_bundle()
00120 {
00121     lcp_options *go = &lcp_gotoptions[0];
00122     lcp_options *ho = &lcp_hisoptions[0];
00123     lcp_options *ao = &lcp_allowoptions[0];
00124     int unit, pppd_pid;
00125     int l, mtu;
00126     char *p;
00127     TDB_DATA key, pid, rec;
00128 
00129     if (doing_multilink) {
00130         /* have previously joined a bundle */
00131         if (!go->neg_mrru || !ho->neg_mrru) {
00132             notice("oops, didn't get multilink on renegotiation");
00133             lcp_close(pcb, "multilink required");
00134             return 0;
00135         }
00136         /* XXX should check the peer_authname and ho->endpoint
00137            are the same as previously */
00138         return 0;
00139     }
00140 
00141     if (!go->neg_mrru || !ho->neg_mrru) {
00142         /* not doing multilink */
00143         if (go->neg_mrru)
00144             notice("oops, multilink negotiated only for receive");
00145         mtu = ho->neg_mru? ho->mru: PPP_MRU;
00146         if (mtu > ao->mru)
00147             mtu = ao->mru;
00148         if (demand) {
00149             /* already have a bundle */
00150             cfg_bundle(0, 0, 0, 0);
00151             netif_set_mtu(pcb, mtu);
00152             return 0;
00153         }
00154         make_new_bundle(0, 0, 0, 0);
00155         set_ifunit(1);
00156         netif_set_mtu(pcb, mtu);
00157         return 0;
00158     }
00159 
00160     doing_multilink = 1;
00161 
00162     /*
00163      * Find the appropriate bundle or join a new one.
00164      * First we make up a name for the bundle.
00165      * The length estimate is worst-case assuming every
00166      * character has to be quoted.
00167      */
00168     l = 4 * strlen(peer_authname) + 10;
00169     if (ho->neg_endpoint)
00170         l += 3 * ho->endpoint.length + 8;
00171     if (bundle_name)
00172         l += 3 * strlen(bundle_name) + 2;
00173     bundle_id = malloc(l);
00174     if (bundle_id == 0)
00175         novm("bundle identifier");
00176 
00177     p = bundle_id;
00178     p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
00179     if (ho->neg_endpoint || bundle_name)
00180         *p++ = '/';
00181     if (ho->neg_endpoint)
00182         p += slprintf(p, bundle_id+l-p, "%s",
00183                   epdisc_to_str(&ho->endpoint));
00184     if (bundle_name)
00185         p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
00186 
00187     /* Make the key for the list of links belonging to the bundle */
00188     l = p - bundle_id;
00189     blinks_id = malloc(l + 7);
00190     if (blinks_id == NULL)
00191         novm("bundle links key");
00192     slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
00193 
00194     /*
00195      * For demand mode, we only need to configure the bundle
00196      * and attach the link.
00197      */
00198     mtu = LWIP_MIN(ho->mrru, ao->mru);
00199     if (demand) {
00200         cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
00201         netif_set_mtu(pcb, mtu);
00202         script_setenv("BUNDLE", bundle_id + 7, 1);
00203         return 0;
00204     }
00205 
00206     /*
00207      * Check if the bundle ID is already in the database.
00208      */
00209     unit = -1;
00210     lock_db();
00211     key.dptr = bundle_id;
00212     key.dsize = p - bundle_id;
00213     pid = tdb_fetch(pppdb, key);
00214     if (pid.dptr != NULL) {
00215         /* bundle ID exists, see if the pppd record exists */
00216         rec = tdb_fetch(pppdb, pid);
00217         if (rec.dptr != NULL && rec.dsize > 0) {
00218             /* make sure the string is null-terminated */
00219             rec.dptr[rec.dsize-1] = 0;
00220             /* parse the interface number */
00221             parse_num(rec.dptr, "IFNAME=ppp", &unit);
00222             /* check the pid value */
00223             if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
00224                 || !process_exists(pppd_pid)
00225                 || !owns_unit(pid, unit))
00226                 unit = -1;
00227             free(rec.dptr);
00228         }
00229         free(pid.dptr);
00230     }
00231 
00232     if (unit >= 0) {
00233         /* attach to existing unit */
00234         if (bundle_attach(unit)) {
00235             set_ifunit(0);
00236             script_setenv("BUNDLE", bundle_id + 7, 0);
00237             make_bundle_links(1);
00238             unlock_db();
00239             info("Link attached to %s", ifname);
00240             return 1;
00241         }
00242         /* attach failed because bundle doesn't exist */
00243     }
00244 
00245     /* we have to make a new bundle */
00246     make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
00247     set_ifunit(1);
00248     netif_set_mtu(pcb, mtu);
00249     script_setenv("BUNDLE", bundle_id + 7, 1);
00250     make_bundle_links(pcb);
00251     unlock_db();
00252     info("New bundle %s created", ifname);
00253     multilink_master = 1;
00254     return 0;
00255 }
00256 
00257 void mp_exit_bundle()
00258 {
00259     lock_db();
00260     remove_bundle_link();
00261     unlock_db();
00262 }
00263 
00264 static void sendhup(char *str)
00265 {
00266     int pid;
00267 
00268     if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
00269         if (debug)
00270             dbglog("sending SIGHUP to process %d", pid);
00271         kill(pid, SIGHUP);
00272     }
00273 }
00274 
00275 void mp_bundle_terminated()
00276 {
00277     TDB_DATA key;
00278 
00279     bundle_terminating = 1;
00280     upper_layers_down(pcb);
00281     notice("Connection terminated.");
00282 #if PPP_STATS_SUPPORT
00283     print_link_stats();
00284 #endif /* PPP_STATS_SUPPORT */
00285     if (!demand) {
00286         remove_pidfiles();
00287         script_unsetenv("IFNAME");
00288     }
00289 
00290     lock_db();
00291     destroy_bundle();
00292     iterate_bundle_links(sendhup);
00293     key.dptr = blinks_id;
00294     key.dsize = strlen(blinks_id);
00295     tdb_delete(pppdb, key);
00296     unlock_db();
00297 
00298     new_phase(PPP_PHASE_DEAD);
00299 
00300     doing_multilink = 0;
00301     multilink_master = 0;
00302 }
00303 
00304 static void make_bundle_links(int append)
00305 {
00306     TDB_DATA key, rec;
00307     char *p;
00308     char entry[32];
00309     int l;
00310 
00311     key.dptr = blinks_id;
00312     key.dsize = strlen(blinks_id);
00313     slprintf(entry, sizeof(entry), "%s;", db_key);
00314     p = entry;
00315     if (append) {
00316         rec = tdb_fetch(pppdb, key);
00317         if (rec.dptr != NULL && rec.dsize > 0) {
00318             rec.dptr[rec.dsize-1] = 0;
00319             if (strstr(rec.dptr, db_key) != NULL) {
00320                 /* already in there? strange */
00321                 warn("link entry already exists in tdb");
00322                 return;
00323             }
00324             l = rec.dsize + strlen(entry);
00325             p = malloc(l);
00326             if (p == NULL)
00327                 novm("bundle link list");
00328             slprintf(p, l, "%s%s", rec.dptr, entry);
00329         } else {
00330             warn("bundle link list not found");
00331         }
00332         if (rec.dptr != NULL)
00333             free(rec.dptr);
00334     }
00335     rec.dptr = p;
00336     rec.dsize = strlen(p) + 1;
00337     if (tdb_store(pppdb, key, rec, TDB_REPLACE))
00338         error("couldn't %s bundle link list",
00339               append? "update": "create");
00340     if (p != entry)
00341         free(p);
00342 }
00343 
00344 static void remove_bundle_link()
00345 {
00346     TDB_DATA key, rec;
00347     char entry[32];
00348     char *p, *q;
00349     int l;
00350 
00351     key.dptr = blinks_id;
00352     key.dsize = strlen(blinks_id);
00353     slprintf(entry, sizeof(entry), "%s;", db_key);
00354 
00355     rec = tdb_fetch(pppdb, key);
00356     if (rec.dptr == NULL || rec.dsize <= 0) {
00357         if (rec.dptr != NULL)
00358             free(rec.dptr);
00359         return;
00360     }
00361     rec.dptr[rec.dsize-1] = 0;
00362     p = strstr(rec.dptr, entry);
00363     if (p != NULL) {
00364         q = p + strlen(entry);
00365         l = strlen(q) + 1;
00366         memmove(p, q, l);
00367         rec.dsize = p - rec.dptr + l;
00368         if (tdb_store(pppdb, key, rec, TDB_REPLACE))
00369             error("couldn't update bundle link list (removal)");
00370     }
00371     free(rec.dptr);
00372 }
00373 
00374 static void iterate_bundle_links(void (*func)(char *))
00375 {
00376     TDB_DATA key, rec, pp;
00377     char *p, *q;
00378 
00379     key.dptr = blinks_id;
00380     key.dsize = strlen(blinks_id);
00381     rec = tdb_fetch(pppdb, key);
00382     if (rec.dptr == NULL || rec.dsize <= 0) {
00383         error("bundle link list not found (iterating list)");
00384         if (rec.dptr != NULL)
00385             free(rec.dptr);
00386         return;
00387     }
00388     p = rec.dptr;
00389     p[rec.dsize-1] = 0;
00390     while ((q = strchr(p, ';')) != NULL) {
00391         *q = 0;
00392         key.dptr = p;
00393         key.dsize = q - p;
00394         pp = tdb_fetch(pppdb, key);
00395         if (pp.dptr != NULL && pp.dsize > 0) {
00396             pp.dptr[pp.dsize-1] = 0;
00397             func(pp.dptr);
00398         }
00399         if (pp.dptr != NULL)
00400             free(pp.dptr);
00401         p = q + 1;
00402     }
00403     free(rec.dptr);
00404 }
00405 
00406 static int
00407 parse_num(str, key, valp)
00408      char *str;
00409      const char *key;
00410      int *valp;
00411 {
00412     char *p, *endp;
00413     int i;
00414 
00415     p = strstr(str, key);
00416     if (p != 0) {
00417         p += strlen(key);
00418         i = strtol(p, &endp, 10);
00419         if (endp != p && (*endp == 0 || *endp == ';')) {
00420             *valp = i;
00421             return 1;
00422         }
00423     }
00424     return 0;
00425 }
00426 
00427 /*
00428  * Check whether the pppd identified by `key' still owns ppp unit `unit'.
00429  */
00430 static int
00431 owns_unit(key, unit)
00432      TDB_DATA key;
00433      int unit;
00434 {
00435     char ifkey[32];
00436     TDB_DATA kd, vd;
00437     int ret = 0;
00438 
00439     slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
00440     kd.dptr = ifkey;
00441     kd.dsize = strlen(ifkey);
00442     vd = tdb_fetch(pppdb, kd);
00443     if (vd.dptr != NULL) {
00444         ret = vd.dsize == key.dsize
00445             && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
00446         free(vd.dptr);
00447     }
00448     return ret;
00449 }
00450 
00451 static int
00452 get_default_epdisc(ep)
00453      struct epdisc *ep;
00454 {
00455     char *p;
00456     struct hostent *hp;
00457     u32_t addr;
00458 
00459     /* First try for an ethernet MAC address */
00460     p = get_first_ethernet();
00461     if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
00462         ep->class = EPD_MAC;
00463         ep->length = 6;
00464         return 1;
00465     }
00466 
00467     /* see if our hostname corresponds to a reasonable IP address */
00468     hp = gethostbyname(hostname);
00469     if (hp != NULL) {
00470         addr = *(u32_t *)hp->h_addr;
00471         if (!bad_ip_adrs(addr)) {
00472             addr = lwip_ntohl(addr);
00473             if (!LOCAL_IP_ADDR(addr)) {
00474                 ep->class = EPD_IP;
00475                 set_ip_epdisc(ep, addr);
00476                 return 1;
00477             }
00478         }
00479     }
00480 
00481     return 0;
00482 }
00483 
00484 /*
00485  * epdisc_to_str - make a printable string from an endpoint discriminator.
00486  */
00487 
00488 static char *endp_class_names[] = {
00489     "null", "local", "IP", "MAC", "magic", "phone"
00490 };
00491 
00492 char *
00493 epdisc_to_str(ep)
00494      struct epdisc *ep;
00495 {
00496     static char str[MAX_ENDP_LEN*3+8];
00497     u_char *p = ep->value;
00498     int i, mask = 0;
00499     char *q, c, c2;
00500 
00501     if (ep->class == EPD_NULL && ep->length == 0)
00502         return "null";
00503     if (ep->class == EPD_IP && ep->length == 4) {
00504         u32_t addr;
00505 
00506         GETLONG(addr, p);
00507         slprintf(str, sizeof(str), "IP:%I", lwip_htonl(addr));
00508         return str;
00509     }
00510 
00511     c = ':';
00512     c2 = '.';
00513     if (ep->class == EPD_MAC && ep->length == 6)
00514         c2 = ':';
00515     else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
00516         mask = 3;
00517     q = str;
00518     if (ep->class <= EPD_PHONENUM)
00519         q += slprintf(q, sizeof(str)-1, "%s",
00520                   endp_class_names[ep->class]);
00521     else
00522         q += slprintf(q, sizeof(str)-1, "%d", ep->class);
00523     c = ':';
00524     for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
00525         if ((i & mask) == 0) {
00526             *q++ = c;
00527             c = c2;
00528         }
00529         q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
00530     }
00531     return str;
00532 }
00533 
00534 static int hexc_val(int c)
00535 {
00536     if (c >= 'a')
00537         return c - 'a' + 10;
00538     if (c >= 'A')
00539         return c - 'A' + 10;
00540     return c - '0';
00541 }
00542 
00543 int
00544 str_to_epdisc(ep, str)
00545      struct epdisc *ep;
00546      char *str;
00547 {
00548     int i, l;
00549     char *p, *endp;
00550 
00551     for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
00552         int sl = strlen(endp_class_names[i]);
00553         if (strncasecmp(str, endp_class_names[i], sl) == 0) {
00554             str += sl;
00555             break;
00556         }
00557     }
00558     if (i > EPD_PHONENUM) {
00559         /* not a class name, try a decimal class number */
00560         i = strtol(str, &endp, 10);
00561         if (endp == str)
00562             return 0;   /* can't parse class number */
00563         str = endp;
00564     }
00565     ep->class = i;
00566     if (*str == 0) {
00567         ep->length = 0;
00568         return 1;
00569     }
00570     if (*str != ':' && *str != '.')
00571         return 0;
00572     ++str;
00573 
00574     if (i == EPD_IP) {
00575         u32_t addr;
00576         i = parse_dotted_ip(str, &addr);
00577         if (i == 0 || str[i] != 0)
00578             return 0;
00579         set_ip_epdisc(ep, addr);
00580         return 1;
00581     }
00582     if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
00583         ep->length = 6;
00584         return 1;
00585     }
00586 
00587     p = str;
00588     for (l = 0; l < MAX_ENDP_LEN; ++l) {
00589         if (*str == 0)
00590             break;
00591         if (p <= str)
00592             for (p = str; isxdigit(*p); ++p)
00593                 ;
00594         i = p - str;
00595         if (i == 0)
00596             return 0;
00597         ep->value[l] = hexc_val(*str++);
00598         if ((i & 1) == 0)
00599             ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
00600         if (*str == ':' || *str == '.')
00601             ++str;
00602     }
00603     if (*str != 0 || (ep->class == EPD_MAC && l != 6))
00604         return 0;
00605     ep->length = l;
00606     return 1;
00607 }
00608 
00609 #endif /* PPP_SUPPORT && HAVE_MULTILINK */