/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * The WvIPRoute and WvIPRouteList class, a quick (mostly hackish) attempt * at a way to read the Linux kernel routing table. */ #include "wviproute.h" #include "wvpipe.h" #include "wvinterface.h" #include "wvfile.h" #include "wvstringlist.h" #include #include WvIPRoute::WvIPRoute(WvStringParm _ifc, const WvIPNet &_net, const WvIPAddr &_gate, int _metric, WvStringParm _table) : ifc(_ifc), ip(_net), gateway(_gate), table(_table), src() { metric = _metric; } WvIPRoute::operator WvString() const { WvIPAddr zero; return WvString("%s via %s %s %s metric %s%s", ip, ifc, gateway, (src != zero ? WvString("src %s", src) : WvString("")), metric, (table != "default") ? WvString(" (table %s)", table) : WvString("")); } bool WvIPRoute::operator== (const WvIPRoute &r2) const { return (ip.network() == r2.ip.network() && ip.netmask() == r2.ip.netmask() && gateway == r2.gateway && ifc == r2.ifc && metric == r2.metric && table == r2.table); } /////////////////////////////////////// WvIPRouteList WvIPRouteList::WvIPRouteList() : log("Route Table", WvLog::Debug) { // nothing else to do } // Reads the kernel routing table, from /proc/net/route, and parses it into // A WvIPRouteList. Also reads the kernel 2.1.x "policy routing" tables, // (via the "ip" command) and parses those routes. void WvIPRouteList::get_kernel() { char *line; WvString ifc, table, gate, addr, mask, src; int metric, flags; bool invalid; WvIPRoute *r; WvStringList words; WvStringList::Iter word(words); // read each route information line from /proc/net/route; even though // "ip route list table all" returns all the same information plus more, // there's no guarantee that the ip command is available on all systems. WvFile kinfo("/proc/net/route", O_RDONLY); kinfo.getline(); while ((line = kinfo.getline()) != NULL) { //log(WvLog::Debug2, "get_kern1: line: %s\n", line); words.zap(); words.split(line); if (words.count() < 10) continue; // weird entry word.rewind(); word.next(); ifc = *word; word.next(); addr = *word; word.next(); gate = *word; word.next(); flags = strtoul(*word, NULL, 16); word.next(); // refcnt word.next(); // use word.next(); metric = atoi(*word); word.next(); mask = *word; // routes appear in the list even when not "up" -- strange. if (!(flags & RTF_UP)) continue; // the addresses in /proc/net/route are in hex. This here is some // pretty sicky type-munging... uint32_t a = strtoul(addr, NULL, 16), m = strtoul(mask, NULL, 16); uint32_t g = strtoul(gate, NULL, 16); WvIPAddr aa(a), mm(m); WvIPNet net(aa, mm); WvIPAddr gw(g); r = new WvIPRoute(ifc, net, gw, metric, "default"); append(r, true); //log(WvLog::Debug2, "get_kern1: out: %s\n", *r); } // add more data from the kernel "policy routing" default table const char *argv[] = { "ip", "route", "list", "table", "all", NULL }; WvPipe defaults(argv[0], argv, false, true, false); while (defaults.isok() && (line = defaults.blocking_getline(-1)) != NULL) { //log(WvLog::Debug2, "get_kern2: line: %s\n", line); invalid = false; ifc = gate = table = ""; metric = 0; words.zap(); words.split(line); if (words.count() < 3) continue; // weird entry word.rewind(); word.next(); if (*word == "broadcast" || *word == "local") continue; // these lines are weird: skip them WvIPNet net((*word == "default") ? WvString("0/0") : *word); while (word.next()) { WvString word1(*word); if (!word.next()) break; WvString word2(*word); if (word1 == "table") { if (word2 == "local") { invalid = true; // ignore 'local' table - too complex break; } else table = word2; } else if (word1 == "dev") ifc = word2; else if (word1 == "via") gate = word2; else if (word1 == "metric") metric = word2.num(); else if (word1 == "scope") ; // ignore else if (word1 == "proto" && word2 == "kernel") ; // ignore else if (word1 == "src") src = word2; else log(WvLog::Debug, "Unknown keyvalue: '%s' '%s' in (%s)\n", word1, word2, line); // ignore all other words - just use their defaults. } // if no table keyword was given, it's the default "main" table, which // we already read from /proc/net/route. Skip it. if (!table) continue; if (!ifc) { log(WvLog::Debug2, "No interface given for this route; skipped.\n"); continue; } r = new WvIPRoute(ifc, net, gate ? WvIPAddr(gate) : WvIPAddr(), metric, table); if (!!src) r->src = src; append(r, true); //log(WvLog::Debug2, "get_kern2: out: %s\n", *r); } } static WvString realtable(WvIPRoute &r) { if (!r.ip.is_default() && r.table == "default") return "main"; else return r.table; } // we use an n-squared algorithm here, for no better reason than readability. void WvIPRouteList::set_kernel() { WvIPRouteList old_kern; old_kern.get_kernel(); Iter oi(old_kern), ni(*this); // FIXME!! // Kernel 2.1.131: deleting a route with no gateway causes the kernel // to delete the _first_ route to that network, regardless of its // gateway. This is probably to make things like "route del default" // more convenient. However, it messes up if we add routes first, then // delete routes. // // Except for this problem, it makes more sense to add and then delete, // since we avoid races (we never completely remove a route to a host // we should be routing to). // delete outdated routes. for (oi.rewind(); oi.next(); ) { if (oi->metric == 99) continue; // "magic" metric for manual override for (ni.rewind(); ni.next(); ) if (*ni == *oi) break; if (!ni.cur()) // hit end of list without finding a match { WvInterface i(oi->ifc); log("Del %s\n", *oi); i.delroute(oi->ip, oi->gateway, oi->metric, realtable(*oi)); } } // add any new routes. for (ni.rewind(); ni.next(); ) { for (oi.rewind(); oi.next(); ) if (*oi == *ni) break; if (!oi.cur()) // hit end of list without finding a match { WvInterface i(ni->ifc); log("Add %s\n", *ni); i.addroute(ni->ip, ni->gateway, ni->src, ni->metric, realtable(*ni)); } } } WvIPRoute *WvIPRouteList::find(const WvIPAddr &addr) { Iter i(*this); for (i.rewind(); i.next(); ) { if (i->ip.includes(addr)) return &i(); } return NULL; }