WvStreams
wviproute.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * The WvIPRoute and WvIPRouteList class, a quick (mostly hackish) attempt
00006  * at a way to read the Linux kernel routing table.
00007  */
00008 #include "wviproute.h"
00009 #include "wvpipe.h"
00010 #include "wvinterface.h"
00011 #include "wvfile.h"
00012 #include "wvstringlist.h"
00013 
00014 #include <net/route.h>
00015 #include <ctype.h>
00016 
00017 WvIPRoute::WvIPRoute(WvStringParm _ifc, const WvIPNet &_net,
00018                      const WvIPAddr &_gate, int _metric,
00019                      WvStringParm _table)
00020     : ifc(_ifc), ip(_net), gateway(_gate), table(_table), src()
00021 {
00022     metric = _metric;
00023 }
00024 
00025 
00026 WvIPRoute::operator WvString() const
00027 {
00028     WvIPAddr zero;
00029     return WvString("%s via %s %s %s metric %s%s",
00030                     ip, ifc, gateway, 
00031                     (src != zero ? WvString("src %s", src) : WvString("")),
00032                     metric,
00033                     (table != "default") 
00034                       ? WvString(" (table %s)", table) : WvString(""));
00035 }
00036 
00037 
00038 bool WvIPRoute::operator== (const WvIPRoute &r2) const
00039 {
00040     return (ip.network() == r2.ip.network() && ip.netmask() == r2.ip.netmask()
00041             && gateway == r2.gateway 
00042             && ifc == r2.ifc && metric == r2.metric
00043             && table == r2.table);
00044 }
00045 
00046 
00047 
00049 
00050 
00051 WvIPRouteList::WvIPRouteList() : log("Route Table", WvLog::Debug)
00052 {
00053     // nothing else to do
00054 }
00055 
00056 
00057 // Reads the kernel routing table, from /proc/net/route, and parses it into
00058 // A WvIPRouteList.  Also reads the kernel 2.1.x "policy routing" tables, 
00059 // (via the "ip" command) and parses those routes.
00060 void WvIPRouteList::get_kernel()
00061 {
00062     char *line;
00063     WvString ifc, table, gate, addr, mask, src;
00064     int metric, flags;
00065     bool invalid;
00066     WvIPRoute *r;
00067     WvStringList words;
00068     WvStringList::Iter word(words);
00069     
00070     // read each route information line from /proc/net/route; even though
00071     // "ip route list table all" returns all the same information plus more,
00072     // there's no guarantee that the ip command is available on all systems.
00073     WvFile kinfo("/proc/net/route", O_RDONLY);
00074     kinfo.getline();
00075     while ((line = kinfo.getline()) != NULL)
00076     {
00077         //log(WvLog::Debug2, "get_kern1: line: %s\n", line);
00078         
00079         words.zap();
00080         words.split(line);
00081         
00082         if (words.count() < 10)
00083             continue; // weird entry
00084         
00085         word.rewind();
00086         word.next(); ifc   = *word;
00087         word.next(); addr  = *word;
00088         word.next(); gate  = *word;
00089         word.next(); flags = strtoul(*word, NULL, 16);
00090         word.next(); // refcnt
00091         word.next(); // use
00092         word.next(); metric = atoi(*word);
00093         word.next(); mask   = *word;
00094         
00095         // routes appear in the list even when not "up" -- strange.
00096         if (!(flags & RTF_UP))
00097             continue;
00098         
00099         // the addresses in /proc/net/route are in hex.  This here is some
00100         // pretty sicky type-munging...
00101         uint32_t a = strtoul(addr, NULL, 16), m = strtoul(mask, NULL, 16);
00102         uint32_t g = strtoul(gate, NULL, 16);
00103         WvIPAddr aa(a), mm(m);
00104         WvIPNet net(aa, mm);
00105         WvIPAddr gw(g);
00106         
00107         r = new WvIPRoute(ifc, net, gw, metric, "default");
00108         append(r, true);
00109         //log(WvLog::Debug2, "get_kern1:  out: %s\n", *r);
00110     }
00111     
00112     // add more data from the kernel "policy routing" default table
00113     const char *argv[] = { "ip", "route", "list", "table", "all", NULL };
00114     WvPipe defaults(argv[0], argv, false, true, false);
00115     while (defaults.isok() && (line = defaults.blocking_getline(-1)) != NULL)
00116     {
00117         //log(WvLog::Debug2, "get_kern2: line: %s\n", line);
00118         
00119         invalid = false;
00120         ifc = gate = table = "";
00121         metric = 0;
00122         
00123         words.zap();
00124         words.split(line);
00125         
00126         if (words.count() < 3)
00127             continue; // weird entry
00128         
00129         word.rewind();
00130         word.next();
00131         if (*word == "broadcast" || *word == "local")
00132             continue; // these lines are weird: skip them
00133 
00134         WvIPNet net((*word == "default") ? WvString("0/0") : *word);
00135         
00136         while (word.next())
00137         {
00138             WvString word1(*word);
00139             if (!word.next()) break;
00140             WvString word2(*word);
00141             
00142             if (word1 == "table")
00143             {
00144                 if (word2 == "local")
00145                 {
00146                     invalid = true; // ignore 'local' table - too complex
00147                     break;
00148                 }
00149                 else
00150                     table = word2;
00151             }
00152             else if (word1 == "dev")
00153                 ifc = word2;
00154             else if (word1 == "via")
00155                 gate = word2;
00156             else if (word1 == "metric")
00157                 metric = word2.num();
00158             else if (word1 == "scope")
00159                 ; // ignore
00160             else if (word1 == "proto" && word2 == "kernel")
00161                 ; // ignore
00162             else if (word1 == "src")
00163                 src = word2;
00164             else
00165                 log(WvLog::Debug, "Unknown keyvalue: '%s' '%s' in (%s)\n",
00166                     word1, word2, line);
00167             
00168             // ignore all other words - just use their defaults.
00169         }
00170         
00171         // if no table keyword was given, it's the default "main" table, which
00172         // we already read from /proc/net/route.  Skip it.
00173         if (!table)
00174             continue;
00175         
00176         if (!ifc)
00177         {
00178             log(WvLog::Debug2, "No interface given for this route; skipped.\n");
00179             continue;
00180         }
00181         
00182         r = new WvIPRoute(ifc, net, gate ? WvIPAddr(gate) : WvIPAddr(),
00183                           metric, table);
00184         if (!!src)
00185                 r->src = src;
00186         append(r, true);
00187         //log(WvLog::Debug2, "get_kern2:  out: %s\n", *r);
00188     }
00189 }
00190 
00191 
00192 static WvString realtable(WvIPRoute &r)
00193 {
00194     if (!r.ip.is_default() && r.table == "default")
00195         return "main";
00196     else
00197         return r.table;
00198 }
00199 
00200 
00201 // we use an n-squared algorithm here, for no better reason than readability.
00202 void WvIPRouteList::set_kernel()
00203 {
00204     WvIPRouteList old_kern;
00205     old_kern.get_kernel();
00206     
00207     Iter oi(old_kern), ni(*this);
00208     
00209     // FIXME!!
00210     // Kernel 2.1.131: deleting a route with no gateway causes the kernel
00211     // to delete the _first_ route to that network, regardless of its
00212     // gateway.  This is probably to make things like "route del default"
00213     // more convenient.  However, it messes up if we add routes first, then
00214     // delete routes.
00215     //
00216     // Except for this problem, it makes more sense to add and then delete,
00217     // since we avoid races (we never completely remove a route to a host
00218     // we should be routing to).
00219 
00220     // delete outdated routes.
00221     for (oi.rewind(); oi.next(); )
00222     {
00223         if (oi->metric == 99) continue; // "magic" metric for manual override
00224         
00225         for (ni.rewind(); ni.next(); )
00226             if (*ni == *oi) break;
00227         
00228         if (!ni.cur()) // hit end of list without finding a match
00229         {
00230             WvInterface i(oi->ifc);
00231             log("Del %s\n", *oi);
00232             i.delroute(oi->ip, oi->gateway, oi->metric, realtable(*oi));
00233         }
00234     }
00235 
00236     // add any new routes.
00237     for (ni.rewind(); ni.next(); )
00238     {
00239         for (oi.rewind(); oi.next(); )
00240             if (*oi == *ni) break;
00241         
00242         if (!oi.cur()) // hit end of list without finding a match
00243         {
00244             WvInterface i(ni->ifc);
00245             log("Add %s\n", *ni);
00246             i.addroute(ni->ip, ni->gateway, ni->src, ni->metric, 
00247                         realtable(*ni));
00248         }
00249     }
00250 }
00251 
00252 
00253 WvIPRoute *WvIPRouteList::find(const WvIPAddr &addr)
00254 {
00255     Iter i(*this);
00256     
00257     for (i.rewind(); i.next(); )
00258     {
00259         if (i->ip.includes(addr))
00260             return &i();
00261     }
00262     
00263     return NULL;
00264 }