WvStreams
wviproute.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * The WvIPRoute and WvIPRouteList class, a quick (mostly hackish) attempt
6  * at a way to read the Linux kernel routing table.
7  */
8 #include "wviproute.h"
9 #include "wvpipe.h"
10 #include "wvinterface.h"
11 #include "wvfile.h"
12 #include "wvstringlist.h"
13 
14 #include <net/route.h>
15 #include <ctype.h>
16 
17 WvIPRoute::WvIPRoute(WvStringParm _ifc, const WvIPNet &_net,
18  const WvIPAddr &_gate, int _metric,
19  WvStringParm _table)
20  : ifc(_ifc), ip(_net), gateway(_gate), table(_table), src()
21 {
22  metric = _metric;
23 }
24 
25 
26 WvIPRoute::operator WvString() const
27 {
28  WvIPAddr zero;
29  return WvString("%s via %s %s %s metric %s%s",
30  ip, ifc, gateway,
31  (src != zero ? WvString("src %s", src) : WvString("")),
32  metric,
33  (table != "default")
34  ? WvString(" (table %s)", table) : WvString(""));
35 }
36 
37 
38 bool WvIPRoute::operator== (const WvIPRoute &r2) const
39 {
40  return (ip.network() == r2.ip.network() && ip.netmask() == r2.ip.netmask()
41  && gateway == r2.gateway
42  && ifc == r2.ifc && metric == r2.metric
43  && table == r2.table);
44 }
45 
46 
47 
49 
50 
51 WvIPRouteList::WvIPRouteList() : log("Route Table", WvLog::Debug)
52 {
53  // nothing else to do
54 }
55 
56 
57 // Reads the kernel routing table, from /proc/net/route, and parses it into
58 // A WvIPRouteList. Also reads the kernel 2.1.x "policy routing" tables,
59 // (via the "ip" command) and parses those routes.
61 {
62  char *line;
63  WvString ifc, table, gate, addr, mask, src;
64  int metric, flags;
65  bool invalid;
66  WvIPRoute *r;
67  WvStringList words;
68  WvStringList::Iter word(words);
69 
70  // read each route information line from /proc/net/route; even though
71  // "ip route list table all" returns all the same information plus more,
72  // there's no guarantee that the ip command is available on all systems.
73  WvFile kinfo("/proc/net/route", O_RDONLY);
74  kinfo.getline();
75  while ((line = kinfo.getline()) != NULL)
76  {
77  //log(WvLog::Debug2, "get_kern1: line: %s\n", line);
78 
79  words.zap();
80  words.split(line);
81 
82  if (words.count() < 10)
83  continue; // weird entry
84 
85  word.rewind();
86  word.next(); ifc = *word;
87  word.next(); addr = *word;
88  word.next(); gate = *word;
89  word.next(); flags = strtoul(*word, NULL, 16);
90  word.next(); // refcnt
91  word.next(); // use
92  word.next(); metric = atoi(*word);
93  word.next(); mask = *word;
94 
95  // routes appear in the list even when not "up" -- strange.
96  if (!(flags & RTF_UP))
97  continue;
98 
99  // the addresses in /proc/net/route are in hex. This here is some
100  // pretty sicky type-munging...
101  uint32_t a = strtoul(addr, NULL, 16), m = strtoul(mask, NULL, 16);
102  uint32_t g = strtoul(gate, NULL, 16);
103  WvIPAddr aa(a), mm(m);
104  WvIPNet net(aa, mm);
105  WvIPAddr gw(g);
106 
107  r = new WvIPRoute(ifc, net, gw, metric, "default");
108  append(r, true);
109  //log(WvLog::Debug2, "get_kern1: out: %s\n", *r);
110  }
111 
112  // add more data from the kernel "policy routing" default table
113  const char *argv[] = { "ip", "route", "list", "table", "all", NULL };
114  WvPipe defaults(argv[0], argv, false, true, false);
115  while (defaults.isok() && (line = defaults.blocking_getline(-1)) != NULL)
116  {
117  //log(WvLog::Debug2, "get_kern2: line: %s\n", line);
118 
119  invalid = false;
120  ifc = gate = table = "";
121  metric = 0;
122 
123  words.zap();
124  words.split(line);
125 
126  if (words.count() < 3)
127  continue; // weird entry
128 
129  word.rewind();
130  word.next();
131  if (*word == "broadcast" || *word == "local")
132  continue; // these lines are weird: skip them
133 
134  WvIPNet net((*word == "default") ? WvString("0/0") : *word);
135 
136  while (word.next())
137  {
138  WvString word1(*word);
139  if (!word.next()) break;
140  WvString word2(*word);
141 
142  if (word1 == "table")
143  {
144  if (word2 == "local")
145  {
146  invalid = true; // ignore 'local' table - too complex
147  break;
148  }
149  else
150  table = word2;
151  }
152  else if (word1 == "dev")
153  ifc = word2;
154  else if (word1 == "via")
155  gate = word2;
156  else if (word1 == "metric")
157  metric = word2.num();
158  else if (word1 == "scope")
159  ; // ignore
160  else if (word1 == "proto" && word2 == "kernel")
161  ; // ignore
162  else if (word1 == "src")
163  src = word2;
164  else
165  log(WvLog::Debug, "Unknown keyvalue: '%s' '%s' in (%s)\n",
166  word1, word2, line);
167 
168  // ignore all other words - just use their defaults.
169  }
170 
171  // if no table keyword was given, it's the default "main" table, which
172  // we already read from /proc/net/route. Skip it.
173  if (!table)
174  continue;
175 
176  if (!ifc)
177  {
178  log(WvLog::Debug2, "No interface given for this route; skipped.\n");
179  continue;
180  }
181 
182  r = new WvIPRoute(ifc, net, gate ? WvIPAddr(gate) : WvIPAddr(),
183  metric, table);
184  if (!!src)
185  r->src = src;
186  append(r, true);
187  //log(WvLog::Debug2, "get_kern2: out: %s\n", *r);
188  }
189 }
190 
191 
192 static WvString realtable(WvIPRoute &r)
193 {
194  if (!r.ip.is_default() && r.table == "default")
195  return "main";
196  else
197  return r.table;
198 }
199 
200 
201 // we use an n-squared algorithm here, for no better reason than readability.
203 {
204  WvIPRouteList old_kern;
205  old_kern.get_kernel();
206 
207  Iter oi(old_kern), ni(*this);
208 
209  // FIXME!!
210  // Kernel 2.1.131: deleting a route with no gateway causes the kernel
211  // to delete the _first_ route to that network, regardless of its
212  // gateway. This is probably to make things like "route del default"
213  // more convenient. However, it messes up if we add routes first, then
214  // delete routes.
215  //
216  // Except for this problem, it makes more sense to add and then delete,
217  // since we avoid races (we never completely remove a route to a host
218  // we should be routing to).
219 
220  // delete outdated routes.
221  for (oi.rewind(); oi.next(); )
222  {
223  if (oi->metric == 99) continue; // "magic" metric for manual override
224 
225  for (ni.rewind(); ni.next(); )
226  if (*ni == *oi) break;
227 
228  if (!ni.cur()) // hit end of list without finding a match
229  {
230  WvInterface i(oi->ifc);
231  log("Del %s\n", *oi);
232  i.delroute(oi->ip, oi->gateway, oi->metric, realtable(*oi));
233  }
234  }
235 
236  // add any new routes.
237  for (ni.rewind(); ni.next(); )
238  {
239  for (oi.rewind(); oi.next(); )
240  if (*oi == *ni) break;
241 
242  if (!oi.cur()) // hit end of list without finding a match
243  {
244  WvInterface i(ni->ifc);
245  log("Add %s\n", *ni);
246  i.addroute(ni->ip, ni->gateway, ni->src, ni->metric,
247  realtable(*ni));
248  }
249  }
250 }
251 
252 
254 {
255  Iter i(*this);
256 
257  for (i.rewind(); i.next(); )
258  {
259  if (i->ip.includes(addr))
260  return &i();
261  }
262 
263  return NULL;
264 }
WvIPNet
An IP network comprises two WvIPAddr structures: an address and a netmask.
Definition: wvaddr.h:312
WvIPRouteList
List of IP Routes currently in effect.
Definition: wviproute.h:36
WvFdStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
WvStream::blocking_getline
char * blocking_getline(time_t wait_msec, int separator='\n', int readahead=1024)
This is a version of getline() that allows you to block for more data to arrive.
Definition: wvstream.cc:602
WvIPRouteList::get_kernel
void get_kernel()
automatically fill the list with appropriate data from the kernel
Definition: wviproute.cc:60
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvPipe
Implementation of a WvPipe stream.
Definition: wvpipe.h:32
WvIPNet::is_default
bool is_default() const
is this net the default gateway? (0.0.0.0/0)
Definition: wvaddr.h:376
WvIPRoute
Manipulate the kernel routing table in strange and interesting ways ;)
Definition: wviproute.h:16
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvIPRouteList::set_kernel
void set_kernel()
automatically set the kernel to the values in the RouteList
Definition: wviproute.cc:202
WvLog
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:56
WvStream::getline
char * getline(time_t wait_msec=0, char separator='\n', int readahead=1024)
Read up to one line of data from the stream and return a pointer to the internal buffer containing th...
Definition: wvstream.h:175
WvInterface::addroute
int addroute(const WvIPNet &dest, int metric=0, WvStringParm table="default")
add a route to the given network through this interface.
Definition: wvinterface.cc:472
WvInterface
A WvInterface manages a particular network interface.
Definition: wvinterface.h:24
WvIPAddr
An IP address is made up of a "dotted quad" – four decimal numbers in the form www....
Definition: wvaddr.h:249
WvInterface::delroute
int delroute(const WvIPNet &dest, int metric=0, WvStringParm table="default")
delete a route to the given network through this interface.
Definition: wvinterface.cc:532
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
WvStringList::split
void split(WvStringParm s, const char *splitchars=" \t\r\n", int limit=0)
split s and form a list ignoring splitchars (except at beginning and end) ie.
Definition: wvstringlist.cc:19
WvFastString::num
int num() const
Return a stdc++ string with the contents of this string.
Definition: wvstring.h:286
WvIPRouteList::find
WvIPRoute * find(const WvIPAddr &addr)
find the routing entry that matches 'addr'
Definition: wviproute.cc:253