1 | #include <iostream> |
---|
2 | #include <fstream> |
---|
3 | #include <sstream> |
---|
4 | #include <string> |
---|
5 | #include <vector> |
---|
6 | #include <set> |
---|
7 | #include <map> |
---|
8 | #include <deque> |
---|
9 | #include <iterator> |
---|
10 | |
---|
11 | #include <getopt.h> |
---|
12 | |
---|
13 | #include "graph.h" |
---|
14 | #include "routing.h" |
---|
15 | |
---|
16 | #include "net.h" |
---|
17 | #include "route_table.h" |
---|
18 | |
---|
19 | typedef set<string> addr_set_t; |
---|
20 | typedef map<string, string> addr_map_t; |
---|
21 | typedef map<string, set<string> > addr_set_map_t; |
---|
22 | |
---|
23 | using namespace std; |
---|
24 | |
---|
25 | // Functor called by a for_each to remove a node (address) from the graph |
---|
26 | class node_remover : public unary_function<string, void> { |
---|
27 | protected: |
---|
28 | graph &g; |
---|
29 | public: |
---|
30 | node_remover(graph& gg) : g(gg) { } |
---|
31 | void operator()(const string &s) { g.remove_node(s); } |
---|
32 | }; |
---|
33 | |
---|
34 | // Functor called by for_each to remove a node (leaf) from the given set. |
---|
35 | class leaf_remover : public unary_function<string, void> { |
---|
36 | protected: |
---|
37 | addr_set_t &l; |
---|
38 | public: |
---|
39 | leaf_remover(addr_set_t& ll) : l(ll) { } |
---|
40 | void operator()(const string &s) { l.erase(s); } |
---|
41 | }; |
---|
42 | |
---|
43 | // Functor called by for_each to print a node's forwarding table to a file with |
---|
44 | // the name of the node (a parameter) in the outdir specified at object |
---|
45 | // creation time. Also in the object are sets of destination objects, a map |
---|
46 | // from node name to addresses, and the map from interfaces to leaf nodes. The |
---|
47 | // full routing matrix is also a parameter at creation time. |
---|
48 | class node_writer : public unary_function<string, void> { |
---|
49 | protected: |
---|
50 | addr_set_t& addrs; // All destination addresses |
---|
51 | addr_set_map_t& node_addrs; // Node name to address set map |
---|
52 | addr_set_map_t& children; // Leaf nodes attached to an interface |
---|
53 | routing& r; // The routing |
---|
54 | const string& outdir; // The output directory |
---|
55 | bool compress; // True if we are compressing tables |
---|
56 | ostream *verbose; // Pointer to verbouse output stream |
---|
57 | mutable int num; // Number of calls to this functor |
---|
58 | public: |
---|
59 | node_writer(addr_set_t& a, addr_set_map_t& na, addr_set_map_t& c, |
---|
60 | routing& rr, const string& od, bool cm, ostream *v) : |
---|
61 | addrs(a), node_addrs(na), children(c), r(rr), outdir(od), |
---|
62 | compress(cm), verbose(v), num(0) { } |
---|
63 | |
---|
64 | void operator()(const string &n) const { |
---|
65 | string out_fn = outdir + "/" + n ; // destination filename |
---|
66 | ofstream out(out_fn.c_str()); // output stream |
---|
67 | addr_set_t& my_addrs = node_addrs[n]; // this node's addresses |
---|
68 | route_table rt; // The routes to compress |
---|
69 | |
---|
70 | // Build the route table |
---|
71 | for (addr_set_t::const_iterator d = addrs.begin(); |
---|
72 | d != addrs.end(); |
---|
73 | d++) { |
---|
74 | // Find the best route to each destination. Because a node may |
---|
75 | // have multiple addresses, we have to check each outgoing |
---|
76 | // interface and pick the shortest. |
---|
77 | string src; // outgoing interface of best route |
---|
78 | int best = 0; // length of best route |
---|
79 | string nh; // The next hop IP address for shortest route |
---|
80 | |
---|
81 | for (addr_set_t::const_iterator i = my_addrs.begin(); |
---|
82 | i != my_addrs.end(); i++) { |
---|
83 | if (src.empty() || r.path_cost(*i, *d) < best ) { |
---|
84 | src = *i; |
---|
85 | best = r.path_cost(*i, *d); |
---|
86 | } |
---|
87 | } |
---|
88 | |
---|
89 | // Ignore single hop paths unless not compressing. |
---|
90 | if (r.path_cost(src, *d) > 1 || !compress) { |
---|
91 | nh = r.next_hop(src, *d); |
---|
92 | |
---|
93 | if ( !nh.empty() ) { |
---|
94 | // Insert the nodes as the /24 containing them |
---|
95 | Net n(*d); |
---|
96 | n.set_mask(24); |
---|
97 | rt.insert(n, Host(nh)); |
---|
98 | } |
---|
99 | } |
---|
100 | else nh = *d; |
---|
101 | |
---|
102 | // Output children of the destination unless they are this |
---|
103 | // node's children and we are compressing. |
---|
104 | if ( my_addrs.count(*d) == 0 || !compress) { |
---|
105 | addr_set_map_t::iterator c = children.find(*d); |
---|
106 | |
---|
107 | if ( c != children.end()) { |
---|
108 | Host gw = Host(nh); |
---|
109 | for (addr_set_t::const_iterator i = (*c).second.begin(); |
---|
110 | i != (*c).second.end(); i++) |
---|
111 | rt.insert(Net(*i), gw); |
---|
112 | } |
---|
113 | } |
---|
114 | |
---|
115 | } |
---|
116 | // Collapse the route map (if we're compressing) and print it |
---|
117 | if (compress) rt.collapse(); |
---|
118 | out << rt; |
---|
119 | if (verbose && ++num % 10 == 0) |
---|
120 | *verbose << num << '\r'; |
---|
121 | } |
---|
122 | }; |
---|
123 | |
---|
124 | // Functor to output a leaf node's routing table. The table has the same name |
---|
125 | // as the node and is output into outdir. The parent_addr map (node to parent |
---|
126 | // address) is passed in. |
---|
127 | class leaf_writer : public unary_function<string, void> { |
---|
128 | protected: |
---|
129 | addr_map_t& parent_addr; // Parent address map |
---|
130 | addr_set_t& addrs; // All destination addresses |
---|
131 | addr_set_map_t& children; // Leaf nodes attached to an interface |
---|
132 | const string& outdir; // The output directory |
---|
133 | bool compress; // Compress routing tables |
---|
134 | ostream *verbose; // Verbose output |
---|
135 | mutable int num; // Number of calls on this functor |
---|
136 | public: |
---|
137 | leaf_writer(addr_map_t& pa, addr_set_t& a, addr_set_map_t& c, |
---|
138 | const string& od, bool cm, ostream *v ) : |
---|
139 | parent_addr(pa), addrs(a), children(c), outdir(od), compress(cm), |
---|
140 | verbose(v), num(0) { } |
---|
141 | void operator()(const string &n) const { |
---|
142 | string out_fn = outdir + "/" + n; |
---|
143 | ofstream out(out_fn.c_str()); |
---|
144 | |
---|
145 | // Compressed leaves have a single default route |
---|
146 | if (compress) |
---|
147 | out << "10.0.0.0/8 " << parent_addr[n] << endl; |
---|
148 | else { |
---|
149 | // For uncompressed leaves, all thr routes go to the parent, |
---|
150 | // but we have to enumerate them as all addresses and the |
---|
151 | // children of any address with leaves. |
---|
152 | string my_parent = parent_addr[n]; |
---|
153 | |
---|
154 | out << my_parent << "/32 " << my_parent << endl; |
---|
155 | for (addr_set_t::const_iterator d = addrs.begin(); |
---|
156 | d != addrs.end(); d++) { |
---|
157 | out << *d << "/32 " << my_parent << endl; |
---|
158 | |
---|
159 | addr_set_map_t::iterator c = children.find(*d); |
---|
160 | |
---|
161 | if ( c != children.end() ) { |
---|
162 | for (addr_set_t::const_iterator i = (*c).second.begin(); |
---|
163 | i != (*c).second.end(); i++) |
---|
164 | out << *i << "/32 " << my_parent << endl; |
---|
165 | } |
---|
166 | } |
---|
167 | } |
---|
168 | if (verbose && ++num % 10 == 0) |
---|
169 | *verbose << num << '\r'; |
---|
170 | } |
---|
171 | }; |
---|
172 | |
---|
173 | |
---|
174 | void parse_graph_description(graph& g, addr_set_t& nodes, addr_set_t& addrs, |
---|
175 | addr_set_t& leaves, addr_map_t& leaf_addr, addr_set_map_t& node_addrs, |
---|
176 | istream *in=0) { |
---|
177 | // Create the basic data structures from the input format. The input is a |
---|
178 | // series of lines of the form: |
---|
179 | // node: addr,addr,addr |
---|
180 | // or |
---|
181 | // addr: addr,addr,addr |
---|
182 | // |
---|
183 | // The first line lists a node's interfaces and the second the |
---|
184 | // interconnections between addresses. The nodes are captured in nodes and |
---|
185 | // leaves (a potential leaf is in both, nodes with multiple interfaces are |
---|
186 | // only in nodes). Addrs is all addresses, leaf_add maps a leaf node to |
---|
187 | // its only address, node_addrs maps each node or leaf to its address(es). |
---|
188 | // g is the grahp of address interconnections. |
---|
189 | string line; // Currnet line |
---|
190 | if (!in ) in = &cin; |
---|
191 | |
---|
192 | while (getline(*in, line, '\n')) { |
---|
193 | string::size_type p = line.find(':'); // String offsets for parsing |
---|
194 | string::size_type q; // String offsets for parsing |
---|
195 | string name; // Node name |
---|
196 | string ename; // Edge name (an address) |
---|
197 | graph::node *node = 0; // Curren graph node (address) |
---|
198 | bool make_node; // True if this is a node |
---|
199 | addr_set_t my_addrs; // Current set of node addrs |
---|
200 | |
---|
201 | if (p != string::npos) { |
---|
202 | name = line.substr(0, p); |
---|
203 | if (name.find_first_not_of("0123456789.") != string::npos) { |
---|
204 | make_node = true; |
---|
205 | } |
---|
206 | else { |
---|
207 | node = g.get_node(name); |
---|
208 | addrs.insert(name); |
---|
209 | make_node = false; |
---|
210 | } |
---|
211 | } |
---|
212 | else continue; |
---|
213 | p += 2; |
---|
214 | while ( (q = line.find(',', p)) != string::npos) { |
---|
215 | ename = line.substr(p, q-p); |
---|
216 | if (make_node) { |
---|
217 | my_addrs.insert(ename); |
---|
218 | } |
---|
219 | else { |
---|
220 | g.get_node(ename); |
---|
221 | node->add_edge(ename, 1.0); |
---|
222 | } |
---|
223 | p = q+1; |
---|
224 | } |
---|
225 | ename = line.substr(p); |
---|
226 | if (make_node) { |
---|
227 | my_addrs.insert(ename); |
---|
228 | |
---|
229 | nodes.insert(name); |
---|
230 | node_addrs[name] = my_addrs; |
---|
231 | if (my_addrs.size() == 1) { |
---|
232 | // Potential leaf node. No need to cross connect, but we do |
---|
233 | // need to list it and its interfaces as leaves. We won't know |
---|
234 | // if it is a leaf node until we find out if it is on a lan. |
---|
235 | leaves.insert(name); |
---|
236 | leaf_addr[name] = ename; |
---|
237 | } |
---|
238 | else { |
---|
239 | // Not a leaf. Add it to nodes and interconnect its interfaces. |
---|
240 | for (addr_set_t::const_iterator i = node_addrs[name].begin(); |
---|
241 | i != node_addrs[name].end(); i ++) { |
---|
242 | for (addr_set_t::const_iterator j =node_addrs[name].begin(); |
---|
243 | j != node_addrs[name].end(); j ++) { |
---|
244 | if ( *i != *j && !g.has_edge(*i, *j)) { |
---|
245 | graph::node *a = g.get_node(*i); |
---|
246 | g.get_node(*j); |
---|
247 | a->add_edge(*j, 1.0); |
---|
248 | } |
---|
249 | } |
---|
250 | } |
---|
251 | } |
---|
252 | } |
---|
253 | else { |
---|
254 | g.get_node(ename); |
---|
255 | node->add_edge(ename, 1.0); |
---|
256 | } |
---|
257 | } |
---|
258 | } |
---|
259 | |
---|
260 | void trim_leaves(graph &g, addr_set_t& nodes, addr_set_t& leaves, |
---|
261 | addr_map_t& leaf_addr, addr_set_t& addrs, addr_map_t& parent_addr, |
---|
262 | addr_set_map_t& children) { |
---|
263 | // Leaves is the set of nodes that may be leaves - nodes connected to one |
---|
264 | // node. Walk that set, eliminate false positives, and for the real leaves |
---|
265 | // remove them from the node set, the addr_set and set their parent_addr |
---|
266 | // (the upstream interface), and add the leaf to the parent interface's |
---|
267 | // children map. |
---|
268 | |
---|
269 | addr_set_t graph_remove; // nodes to remove from the graph |
---|
270 | addr_set_t leaf_remove; // leaves to remove from the leaves set. |
---|
271 | |
---|
272 | for (addr_set_t::const_iterator l = leaves.begin(); l != leaves.end(); |
---|
273 | l++) { |
---|
274 | string la = leaf_addr[*l]; // leaf address |
---|
275 | graph::node *ln = g.get_node(la); // leaf address node |
---|
276 | if (ln-> degree() == 1 ) { |
---|
277 | graph::edge e = *ln->begin(); // edge |
---|
278 | string pa = e.get_node_name(); // parent address |
---|
279 | addr_set_map_t::iterator c = children.find(pa); // children entry |
---|
280 | |
---|
281 | parent_addr[*l] = pa; |
---|
282 | if (c != children.end() ) |
---|
283 | (*c).second.insert(la); |
---|
284 | else { |
---|
285 | children[pa] = addr_set_t(); |
---|
286 | children[pa].insert(la); |
---|
287 | } |
---|
288 | // This is a confirmed leaf, so get it out of nodes. |
---|
289 | nodes.erase(*l); |
---|
290 | // NB cannot remove parent address as it may be a destination from |
---|
291 | // other palces in the net. |
---|
292 | graph_remove.insert(la); |
---|
293 | addrs.erase(la); |
---|
294 | } |
---|
295 | else { |
---|
296 | // Not a leaf after all, leave it in nodes and remove it from |
---|
297 | // leaves. |
---|
298 | leaf_remove.insert(*l); |
---|
299 | } |
---|
300 | |
---|
301 | } |
---|
302 | // Clean up the data structures we've been pulling things from. |
---|
303 | for_each(graph_remove.begin(), graph_remove.end(), node_remover(g)); |
---|
304 | for_each(leaf_remove.begin(), leaf_remove.end(), leaf_remover(leaves)); |
---|
305 | g.fix_edges(); |
---|
306 | } |
---|
307 | enum { OUTPUT_OPT='o', DEBUG_OPT='d', NOCOMPRESS_OPT='C', INPUT_OPT='f'}; |
---|
308 | struct option opts[] = { |
---|
309 | { "output", required_argument, 0, OUTPUT_OPT}, |
---|
310 | { "input", required_argument, 0, INPUT_OPT}, |
---|
311 | { "debug", no_argument, 0, DEBUG_OPT}, |
---|
312 | { "uncompressed", required_argument, 0, NOCOMPRESS_OPT}, |
---|
313 | }; |
---|
314 | |
---|
315 | void fatal(const string& my_name) { |
---|
316 | cerr << "Usage: " << my_name << |
---|
317 | ": [options] [outputdir [filename]]" << endl; |
---|
318 | exit(20); |
---|
319 | } |
---|
320 | |
---|
321 | |
---|
322 | int main(int argc, char**argv) { |
---|
323 | addr_set_t nodes; // Set of nodenames (not addresses) |
---|
324 | addr_set_t addrs; // All valid destination addresses |
---|
325 | addr_set_t leaves; // Leaf nodes |
---|
326 | addr_map_t parent_addr; // Map from leaf address to parent address |
---|
327 | addr_map_t leaf_addr; // Map from leaf node name to leaf address |
---|
328 | addr_set_map_t node_addrs; // Map from each node to its addresses |
---|
329 | addr_set_map_t children; // Map from a parent addess to its |
---|
330 | // leaf children |
---|
331 | graph g; // Graph represention |
---|
332 | string my_name = argv[0]; // The name of the program |
---|
333 | string outdir; // Output directory |
---|
334 | string uncompressed_dir; // Output for uncompressed routing tables |
---|
335 | int fl; // Option parsing |
---|
336 | bool debug=false; // True for debugging output |
---|
337 | ifstream *inf = 0; // Input file, if any |
---|
338 | |
---|
339 | while (( fl = getopt_long(argc, argv, "o:dC:", opts, 0)) != -1) { |
---|
340 | switch (fl) { |
---|
341 | case DEBUG_OPT: |
---|
342 | debug = true; |
---|
343 | break; |
---|
344 | case OUTPUT_OPT: |
---|
345 | outdir = optarg; |
---|
346 | break; |
---|
347 | case INPUT_OPT: |
---|
348 | if (!(inf = new ifstream(optarg))) { |
---|
349 | cerr << "Cannot open " << optarg << endl; |
---|
350 | fatal(my_name); |
---|
351 | } |
---|
352 | break; |
---|
353 | case NOCOMPRESS_OPT: |
---|
354 | uncompressed_dir=optarg; |
---|
355 | break; |
---|
356 | } |
---|
357 | } |
---|
358 | argc -= optind; |
---|
359 | argv += optind; |
---|
360 | if (argc > 0 && outdir.empty()) { |
---|
361 | outdir = argv[0]; |
---|
362 | argc--; argv++; |
---|
363 | } |
---|
364 | if (argc > 0 && !inf) { |
---|
365 | if (!(inf = new ifstream(optarg))) { |
---|
366 | cerr << "Cannot open " << optarg << endl; |
---|
367 | fatal(my_name); |
---|
368 | } |
---|
369 | argc--; argv++; |
---|
370 | } |
---|
371 | |
---|
372 | if (outdir.empty()) fatal(my_name); |
---|
373 | |
---|
374 | parse_graph_description(g, nodes, addrs, leaves, leaf_addr, node_addrs,inf); |
---|
375 | trim_leaves(g, nodes, leaves, leaf_addr, addrs, parent_addr, children); |
---|
376 | |
---|
377 | routing r = routing(g, debug ? &cerr: 0); |
---|
378 | |
---|
379 | for_each(nodes.begin(), nodes.end(), |
---|
380 | node_writer(addrs, node_addrs, children, r, outdir, true, |
---|
381 | debug ? &cerr : 0)); |
---|
382 | if (debug) cerr << endl; |
---|
383 | for_each(leaves.begin(), leaves.end(), |
---|
384 | leaf_writer(parent_addr, addrs, children, outdir, true, |
---|
385 | debug ? &cerr : 0)); |
---|
386 | if (debug) cerr << endl; |
---|
387 | |
---|
388 | if (!uncompressed_dir.empty()) { |
---|
389 | for_each(nodes.begin(), nodes.end(), |
---|
390 | node_writer(addrs, node_addrs, children, r, |
---|
391 | uncompressed_dir, false, debug ? &cerr : 0)); |
---|
392 | if (debug) cerr << endl; |
---|
393 | for_each(leaves.begin(), leaves.end(), |
---|
394 | leaf_writer(parent_addr, addrs, children, |
---|
395 | uncompressed_dir, false, debug ? &cerr : 0)); |
---|
396 | if (debug) cerr << endl; |
---|
397 | } |
---|
398 | |
---|
399 | } |
---|